[航海协会]SSSP

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[航海协会]SSSP相关的知识,希望对你有一定的参考价值。

SSSP

题目概述


题解

首先让我们看看这道题,应该很容易先想到一个比较暴力的思路,就是不断让所有点都往前走一条边,然后维护到所有点前 K K K小的路径长度。
显然,再之后的路径无论怎么走都无法成为前 K K K小了,无论如何都至少有 K K K条比它们小的路径,所以只维护前 K K K小即可。
但这样做显然是 O ( q m K 2 ) O\\left(qmK^2\\right) O(qmK2)的,考虑怎么优化。
可以发现,它们的转移都是符合矩阵的性质的,可以考虑把转移放到矩阵上去,然后就可以用矩阵维护了,先预处理出倍增矩阵,这样询问的时候也就只需要乘向量。
复杂度成功被优化到了 O ( ( n + q ) n 2 K 2 log ⁡ a + m K ) O\\left((n+q)n^2K^2\\log a+mK\\right) O((n+q)n2K2loga+mK),显然还是跑不过考虑继续优化。

可以发现, ∑ k = 1 n M i , k M k , j \\sum_k=1^nM_i,kM_k,j k=1nMi,kMk,j都是会转移贡献到 M i , j ′ M'_i,j Mi,j,我们只用维护它的前 K K K小,那么一种经典方法就是先将 M i , k M k , j M_i,kM_k,j Mi,kMk,j的最小的值放到堆里面去,每次选出所有数中最小的一个,放到 M i , j ′ M'_i,j Mi,j里面去,它是由 M i , k M k , j M_i,kM_k,j Mi,kMk,j构成的,然后将 M i , k M_i,k Mi,k或者 M k , j M_k,j Mk,j稍微调大一点放到堆里面去。
这样每次稍微调大肯定都是找到的稍微大点的一个,所有都必然在某个时刻被加入。
由于我们只找前 K K K大,所以除了最开始加入的,都只会再加入 K K K个。
这样的时间复杂度是 O ( ( n + q ) n ( n + K ) log ⁡ n log ⁡ a + m K ) O\\left((n+q)n(n+K)\\log n\\log a+mK\\right) O((n+q)n(n+K)lognloga+mK)
但它还是会 T T T的,显然,这里的复杂度瓶颈是在那个 n log ⁡ n n\\log n nlogn上面的,由于最开始的元素是比较多的,考虑线性建堆。
也就是后序遍历二叉树,当加入两个儿子的父亲时,看父亲是不是比儿子小,如果是就把较小的儿子调上来,父亲扔下去。
这样的话建堆是 ∑ O ( i n 2 i ) ≈ O ( n ) \\sum O(\\fracin2^i)\\approx O(n) O(2iin)O(n)的,复杂度就被优化到 O ( ( n + q ) n ( n + K log ⁡ n ) log ⁡ a + m K ) O\\left((n+q)n(n+K\\log n)\\log a+mK\\right) O((n+q)n(n+Klogn)loga+mK)了,看上去就很能过了。
但如果你交上去的话你会发现你还是会被卡常了。
观察一下,我们的堆还是太大了,明明值需要前 K K K小,却达到了 O ( n ) O\\left(n\\right) O(n)级别的大小。
考虑 n t h _ e l e m e n t \\rm nth\\_element nth_element对于我们最开始的数组求出前 K K K小再建堆,这样就会快不少。
时间复杂度也就达到了 O ( ( n + q ) n ( n + K log ⁡ K ) log ⁡ a + m K ) O\\left((n+q)n(n+K\\log K)\\log a+mK\\right) O((n+q)n(n+KlogK)loga+mK),基本不会被卡常了。

上面已经说了,时间复杂度 O ( ( n + q ) n ( n + K log ⁡ K ) log ⁡ a + m K ) O\\left((n+q)n(n+K\\log K)\\log a+mK\\right) O((n+q)n(n+KlogK)loga+mK)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXM 2000005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const int INF=0x3f3f3f3f;
template<typename _T>
void read(_T &x)
   _T f=1;x=0;char s=getchar();
   while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
   while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
   x*=f;

template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,m,Q,logtime,tota,totb,root,totc;
struct ming
    int x,y,z,w;ming()
    ming(int X,int Y,int Z,int W)x=X;y=Y;z=Z;w=W;
    bool operator < (const ming &rhs)constreturn w<rhs.w;
a[205];
struct node
    int dis[18];node()
    void clear()for(int i=1;i<=15;i++)dis[i]=INF;
;
class Treap
    private:
        int tot,lson[55],rson[55];ming tr[55];
    public:
        void check(int rt)
            if(!lson[rt]&&!rson[rt])return ;
            int lw=lson[rt]?tr[lson[rt]].w:INF;
            int rw=rson[rt]?tr[rson[rt]].w:INF;
            if(lw<=rw&&lw<tr[rt].w)swap(tr[rt],tr[lson[rt]]),check(lson[rt]);if(tr[lson[rt]].w>INF-1)lson[rt]=0;
            else if(rw<tr[rt].w)swap(tr[rt],tr[rson[rt]]),check(rson[rt]);if(tr[rson[rt]].w>INF-1)rson[rt]=0;
        
        void build(int &rt,int l,int r)
            int mid=l+r>>1;rt=++tot;tr[rt]=a[mid];
            if(l<mid)build(lson[rt],l,mid-1);
            if(r>mid)build(rson[rt],mid+1,r);
            check(rt);
        
        ming ask()return tr[root];
        void pop()tr[root].w=INF;check(root);if(tr[root].w>INF-1)root=0;
        void insert(ming aw)
            if(!root)root=++tot;tr[tot]=aw;return ;
            int now=root[航海协会]树

[航海协会]树

[航海协会]逆天题

[航海协会]逆天题

[航海协会]逆天题

[航海协会]万灵药