旅行
题目概述
题解
题目是叫你求到每个点的最短路,但是显然边有这么多,我们不可能每条边都跑一遍去求最短路呀。
但我们观察连边的形式,显然可以猜测到有许多边都是多余的,那么我们可以考虑去优化建图。
显然,对于树的哪个部分分我们是很容易地可以考虑到使用点分治优化建图的。
由于我们的任意一条树上路径都肯定可以被点分树上的一个子树所覆盖,那么我们可以在点分树的节点上建树高这么多个虚点,像距离该点每一层的节点都连边,这样,我们就可以只连一条边就可以代表原来需要连的所有边。
但对于不是树的情况我们又应该怎样办呢?
我们注意到
m
⩽
n
+
50
m\leqslant n+50
m⩽n+50,也就是说我们先对于一棵生成树建图后,剩下的
50
50
50条边不如对每条边都多建
n
n
n个虚点,随便选一个点当关键点,向整张图用上面的方法连边。
当然,这样就不能直接 dijkstra 了,显然会
T
T
T的,但我们发现该图中许多边都是
0
0
0,也就是说,当我们访问一部分点后,就可以走
0
0
0边立即访问许多虚点。
也就是说,我们真正放在队列里的点,实际上都是只有实点。
于是我们就大大降低了复杂度(?常数),感觉还是有
log
\log
log。反正能过
时间复杂度 O ( n ( n − m + log n ) log n ) O\left(n(n-m+\log\,n)\log n\right) O(n(n−m+logn)logn)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200055
typedef long long LL;
typedef pair<int,int> pii;
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const LL INF=0x3f3f3f3f3f3f3f3f;
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,head[MAXN],tot,c[MAXN],d[MAXN],np[MAXN],totd,fa[MAXN];
int mxson[MAXN],siz[MAXN],dep[MAXN],Rt,N,all,mxx,ord[MAXN],idx;
LL dis[MAXN*70],mx;
bool vis[MAXN],vp[MAXN];
struct ming{int u,v;}s[MAXN],dd[55];
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
queue<int>p;
vector<pii>G[MAXN*70];
struct tann{
int id;LL t;tann(){id=t=0;}
tann(int I,LL T){id=I;t=T;}
bool operator < (const tann &rhs)const{return t>rhs.t;}
};
priority_queue<tann>q;
void getRoot(int u,int fa){
siz[u]=1;mxson[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa||vis[v])continue;
getRoot(v,u);siz[u]+=siz[v];
mxson[u]=max(mxson[u],siz[v]);
}
mxson[u]=max(mxson[u],all-siz[u]);
if(mxson[u]<mx)mx=mxson[u],Rt=u;
}
void dosaka(int u,int fa){
dep[u]=dep[fa]+1;siz[u]=1;mxx=max(mxx,dep[u]);ord[++idx]=u;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(vis[v]||v==fa)continue;
dosaka(v,u);siz[u]+=siz[v];
}
}
void sakura(int rt){
mx=INF;Rt=mxx=idx=0;getRoot(rt,0);dosaka(Rt,0);
for(int i=0;i<mxx;i++)np[i]=++N;
for(int i=1;i<mxx;i++)G[np[i]].pb(mkpr(np[i-1],0));
for(int i=1;i<=idx;i++){
int x=ord[i],tmp=min(d[x]-dep[x]+1,mxx-1);
if(tmp>=0)G[x].pb(mkpr(np[tmp],c[x]));
G[np[dep[x]-1]].pb(mkpr(x,0));
}
vis[Rt]=1;
for(int i=head[Rt];i;i=e[i].nxt){
int v=e[i].to;if(vis[v])continue;
all=siz[v];sakura(v);
}
}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(u^v)fa[u]=v;}
int main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
read(n);read(m);N=n;makeSet(n);
for(int i=1;i<=m;i++)read(s[i].u),read(s[i].v);
for(int i=1;i<=n;i++)read(d[i]),read(c[i]);
for(int i=1;i<=m;i++){
int u=s[i].u,v=s[i].v;
if(findSet(u)==findSet(v)){dd[++totd]=s[i];continue;}
unionSet(u,v);addEdge(u,v);addEdge(v,u);
}
all=n;sakura(1);
for(int i=1;i<=totd;i++)
addEdge(dd[i].u,dd[i].v),
addEdge(dd[i].v,dd[i].u);
for(int i=1;i<=totd;i++){
int S=dd[i].u;for(int j=1;j<=n;j++)dep[j]=0;
while(!p.empty())p.pop();dep[S]=1;p.push(S);mxx=0;
while(!p.empty()){
int u=p.front();p.pop();mxx=max(mxx,dep[u]);
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(dep[v])continue;
dep[v]=dep[u]+1;p.push(v);
}
}
for(int j=0;j<mxx;j++)np[j]=++N;
for(int j=1;j<mxx;j++)G[np[j]].pb(mkpr(np[j-1],0));
for(int j=1;j<=n;j++){
int tmp=min(d[j]-dep[j]+1,mxx-1);
if(tmp>=0)G[j].pb(mkpr(np[tmp],c[j]));
G[np[dep[j]-1]].pb(mkpr(j,0));
}
}
for(int i=1;i<=N;i++)dis[i]=INF;dis[1]=0;
while(!q.empty())q.pop();q.push(tann(1,(LL)c[1]));
while(!q.empty()){
tann t=q.top();q.pop();int u=t.id;
if(dis[u]+c[u]^t.t)continue;LL tmp=t.t;
while(!p.empty())p.pop();p.push(u);
while(!p.empty()){
int su=p.front();p.pop();
int siz=G[su].size();
for(int j=0;j<siz;j++){
int v=G[su][j].fir,w=G[su][j].sec;
if(dis[v]>dis[su]+w){
dis[v]=dis[su]+w;
if(v<=n)q.push(tann(v,dis[v]+c[v]));
else if(dis[v]==tmp)p.push(v);
}
}
}
}
for(int i=2;i<=n;i++)printf("%lld\n",dis[i]);
return 0;
}