[航海协会]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[航海协会]树