问题描述
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;
}