题目描述:
给一张n个点,m条边,有源点S的无向图,求对于每个点,有多少个点的最短路必经过它。n<=100000,m<=300000
解题思路:
先跑一边最短路建出最短路DAG,然后就是求每个点的必经点,直接上支配树即可。
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
#define pb push_back
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005,M=600005;
int n,m,S;
int tot,first[N],nxt[M],to[M],w[M],exist[N];ll dis[N];queue<int>q;
int idx,dfn[N],id[N],fa[N],sdom[N],idom[N],last[N],minp[N],size[N];
vector<int>pre[N],bkt[N],g1[N],g2[N];
void add(int x,int y,int z)
{
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}
void SPFA()
{
for(int i=1;i<=n;i++)dis[i]=1e18;
dis[S]=0,q.push(S),exist[S]=1;
while(!q.empty())
{
int u=q.front();q.pop();exist[u]=0;
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(dis[v]>dis[u]+w[e])
{
dis[v]=dis[u]+w[e];
if(!exist[v])q.push(v),exist[v]=1;
}
}
}
for(int u=1;u<=n;u++)
for(int e=first[u];e;e=nxt[e])if(dis[u]+w[e]==dis[to[e]])
g1[u].pb(to[e]),pre[to[e]].pb(u);
}
void dfs1(int u)
{
id[dfn[u]=sdom[u]=++idx]=u;
for(int v:g1[u])if(!dfn[v])fa[v]=u,dfs1(v);
}
int find(int x)
{
if(last[x]==x)return x;
int fx=find(last[x]);
if(sdom[minp[last[x]]]<sdom[minp[x]])minp[x]=minp[last[x]];
return last[x]=fx;
}
inline int eval(int u){find(u);return minp[u];}
void build()
{
for(int i=1;i<=n;i++)last[i]=minp[i]=i;
for(int i=idx;i>=2;i--)
{
int u=id[i];
for(int v:pre[u])sdom[u]=min(sdom[u],sdom[eval(v)]);
bkt[id[sdom[u]]].pb(u);last[u]=fa[u];
for(int v:bkt[fa[u]])
{
int w=eval(v);
idom[v]=sdom[v]==sdom[w]?fa[u]:w;
}
bkt[fa[u]].clear();
}
for(int i=2,u;i<=idx;i++)
{
u=id[i];
idom[u]=idom[u]==id[sdom[u]]?idom[u]:idom[idom[u]];
g2[idom[u]].pb(u);
}
}
void dfs2(int u)
{
size[u]=1;
for(int v:g2[u])dfs2(v),size[u]+=size[v];
}
int main()
{
//freopen("lx.in","r",stdin);
int x,y,z;
n=getint(),m=getint(),S=getint();
while(m--)
{
x=getint(),y=getint(),z=getint();
add(x,y,z),add(y,x,z);
}
SPFA();dfs1(S);build();dfs2(S);
for(int i=1;i<=n;i++)printf("%d\n",size[i]);
return 0;
}