[硫化铂]旅行

旅行

题目概述

在这里插入图片描述
在这里插入图片描述

题解

题目是叫你求到每个点的最短路,但是显然边有这么多,我们不可能每条边都跑一遍去求最短路呀。
但我们观察连边的形式,显然可以猜测到有许多边都是多余的,那么我们可以考虑去优化建图。
显然,对于树的哪个部分分我们是很容易地可以考虑到使用点分治优化建图的。
由于我们的任意一条树上路径都肯定可以被点分树上的一个子树所覆盖,那么我们可以在点分树的节点上建树高这么多个虚点,像距离该点每一层的节点都连边,这样,我们就可以只连一条边就可以代表原来需要连的所有边。

但对于不是树的情况我们又应该怎样办呢?
我们注意到 m ⩽ n + 50 m\leqslant n+50 mn+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(nm+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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值