[51nod2883] 城市

问题描述

A国是一个拥有n个城市的国家,其中城市s是A国的首都。

A国还有m条道路,每条道路连着两个不同的城市,但是一对城市间可能有多条道路。每一条道路都有它的长度,一条道路的通行时间与一条道路的长度成正比。

你作为A国的统治者,设计出了一种统计城市重要程度的方法:

1、一条道路的重要度为:在这条道路不能使用的情况下,到首都s的最短时间会变长的城市的数目。

2、一个城市的重要度为:以它作为一端的所有道路的重要度的和。

现在,你知道了A国的道路连接情况,你需要计算出每一个城市的重要度。

输入格式

第一行,两个整数n,m,表示有A国有n个城市及m条道路。
第2~m+1行,每行三个整数u,v,l,描述了一条道路的两个端点城市及长度。
第m+2行,一个整数s,表示A国首都的编号

输出格式

n行,每行一个整数,第i行为编号i的城市的重要度。

样例输入

4 4
1 2 3
2 3 4
3 4 5
4 1 2
1

样例输出

2
1
0
1

数据范围

50% 2 <= n <= 5000 2 <= m <= 100000 1 <= l <= 150000
75% 2 <= n <= 100000 2 <= m <= 200000 2 <= l <= 10000000
100% 2 <= n <= 100000 2 <= m <= 200000 1 <= l <= 47718126

解析

考虑什么情况下删除一条边会对某些点的最短路产生影响。一个显而易见的条件是,这个点一定会被S到某个点的最短路经过。由此我们可以联想到将原图的最短路图建出来。利用最短路图是一个DAG的性质,我们能够发现,如果一个点的入度为1,那么删除这个点唯一的入边至少会对这个点的最短路产生影响,然而我们不知道具体会影响对少个点。

在DAG上我们并不好考虑这个问题。假设这个DAG是一棵树,那么删除这一边能够影响到的点就是这条边的终点的子树,问题就方便多了。当然,即使不是一棵树我们也能够转化一下使其变成一棵树。具体的,一个点的父节点是它所有前驱在这棵树上的LCA,也就是从这个点出发到s的最短路上第一个必经点。这样的树显然是合法的,接下来考虑如何建树。

我们首先把最短路图拓扑排序,然后按照拓扑序一边建树一边处理倍增LCA。由于是按照拓扑序处理,当前点的前驱一定是已经在树上了。然后在建好的树上求子树和即可。最后的答案即为每个点相邻的边的答案之和。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
#define int long long
#define N 100002
#define M 200002
using namespace std;
struct Edge{
	int u,id;
	Edge(int _u,int _id){
		u=_u;id=_id;
	}
};
int head[N],ver[M*2],nxt[M*2],edge[M*2],l;
int head1[N],ver1[M*2],nxt1[M*2],d[N],l1;
int n,m,s,i,j,dis[N],ans[M],f[N][32],dep[N],a[N],sum[N];
bool in[N];
vector<Edge> v[N];
int read()
{
	char c=getchar();
	int w=0;
	while(c<'0'||c>'9') c=getchar();
	while(c<='9'&&c>='0'){
		w=w*10+c-'0';
		c=getchar();
	}
	return w;
}
void insert(int x,int y,int z)
{
	ver[l]=y;
	edge[l]=z;
	nxt[l]=head[x];
	head[x]=l;
	l++;
}
void insert1(int x,int y)
{
	l1++;
	ver1[l1]=y;
	nxt1[l1]=head1[x];
	head1[x]=l1;
	d[y]++;
}
void SPFA()
{
	queue<int> q;
	memset(dis,0x3f,sizeof(dis));
	q.push(s);
	dis[s]=0;in[s]=1;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=head[x];i!=-1;i=nxt[i]){
			int y=ver[i];
			if(dis[y]>dis[x]+edge[i]){
				dis[y]=dis[x]+edge[i];
				if(!in[y]){
					q.push(y);
					in[y]=1;
				}
			}
		}
		in[x]=0;
	}
}
void toposort()
{
	int cnt=0;
	queue<int> q;
	for(int i=1;i<=n;i++){
		if(d[i]==0) q.push(i),a[++cnt]=i;
	}
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=head1[x];i;i=nxt1[i]){
			int y=ver1[i];
			d[y]--;
			if(d[y]==0) q.push(y),a[++cnt]=y;
		}
	}
}
int LCA(int u,int v)
{
	if(dep[u]>dep[v]) swap(u,v);
	int tmp=dep[v]-dep[u];
	for(int i=0;(1<<i)<=tmp;i++){
		if(tmp&(1<<i)) v=f[v][i];
	}
	if(u==v) return u;
	for(int i=log2(1.0*n);i>=0;i--){
		if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
	}
	return f[u][0];
}
void dfs(int x)
{
	sum[x]=1;
	for(int i=head1[x];i;i=nxt1[i]){
		int y=ver1[i];
		dfs(y);
		sum[x]+=sum[y];
	}
	if(v[x].size()==1) ans[v[x][0].id]=sum[x];
}
signed main()
{
	memset(head,-1,sizeof(head));
	n=read();m=read();
	for(i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		insert(u,v,w);
		insert(v,u,w);
	}
	s=read();
	SPFA();
	for(i=1;i<=n;i++){
		for(j=head[i];j!=-1;j=nxt[j]){
			if(dis[ver[j]]+edge[j]==dis[i]){
				v[i].push_back(Edge(ver[j],j/2));
				insert1(ver[j],i);
			}
		}
	}
	toposort();
	memset(head1,0,sizeof(head1));l1=0;
	dep[s]=1;
	for(i=2;i<=n;i++){
		int fa=v[a[i]][0].u;
		for(j=1;j<v[a[i]].size();j++) fa=LCA(fa,v[a[i]][j].u);
		f[a[i]][0]=fa;dep[a[i]]=dep[fa]+1;
		insert1(fa,a[i]);
		for(j=0;(1<<(j+1))<=n;j++) f[a[i]][j+1]=f[f[a[i]][j]][j];
	}
	dfs(s);
	for(i=1;i<=n;i++){
		int ret=0;
		for(j=head[i];j!=-1;j=nxt[j]) ret+=ans[j/2];
		printf("%lld\n",ret);
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/LSlzf/p/12663329.html