• [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
    ",ret);
    	}
    	return 0;
    }
    
  • 相关阅读:
    SQL 使用identity(int,1,1)来产生行号。
    SQL DateName\DatePart 返回表示指定date的指定datepart的字符串
    让我们受用一生的好习惯
    SCRUM软件开发过程(转)
    计算机英语词汇
    oral English英语绕口令(转)
    Setup相关经验总结
    与老外吵架之必会109句
    BAT批处理文件语法(转)
    SQL Server 2005之PIVOT/UNPIVOT行列转换(转)
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12663329.html
Copyright © 2020-2023  润新知