来自FallDream的博客,未经允许,请勿转载,谢谢。
给定一张图,求每个点到第n个点必须经过的点的编号之和。n<=50000
一道支配树裸题
然后统计答案的时候可以正着推,ans[i]=ans[idom[i]]+i
#include<iostream> #include<cstdio> #include<cstring> #define MN 50000 #define ll long long using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } ll val[MN+5]; int n,m,cnt,tot=0,mark[MN+5],dn,head[MN+5],Fa[MN+5],fa[MN+5],top[MN+5],back[MN+5],Best[MN+5],dfn[MN+5],id[MN+5],s[MN+5],d[MN+5]; struct edge{int to,next;}e[MN*100]; inline void ins(int*H,int f,int t){e[++cnt]=(edge){t,H[f]};H[f]=cnt;} void Pre(int x) { id[dfn[x]=++dn]=x; for(int i=head[x];i;i=e[i].next) if(!dfn[e[i].to]) Fa[e[i].to]=x,Pre(e[i].to); } inline bool P(int x,int y){return dfn[x]>dfn[y];} inline int Up(int x) { if(fa[x]==x) return x; int y=Up(fa[x]); if(P(s[Best[x]],s[Best[fa[x]]])) Best[x]=Best[fa[x]]; return fa[x]=y; } inline void Tarjan() { for(int i=dn;i>1;--i) { int u=id[i]; for(int j=back[u];j;j=e[j].next) if(dfn[e[j].to]) Up(e[j].to),P(s[u],s[Best[e[j].to]])?s[u]=s[Best[e[j].to]]:0; ins(top,s[u],u);fa[u]=Fa[u];u=Fa[u]; for(int j=top[u];j;j=e[j].next) { Up(e[j].to); if(s[Best[e[j].to]]==s[u]) d[e[j].to]=u; else d[e[j].to]=Best[e[j].to]; } } for(int i=2;i<=dn;++i) if(d[id[i]]!=s[id[i]]) d[id[i]]=d[d[id[i]]]; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { cnt=dn=0;++tot; memset(head,0,sizeof(int)*(n+2)); memset(back,0,sizeof(int)*(n+2)); memset(top,0,sizeof(int)*(n+2)); memset(val,0,sizeof(ll)*(n+2)); memset(dfn,0,sizeof(int)*(n+2)); memset(d,0,sizeof(int)*(n+2)); for(int i=1;i<=m;++i) { int u=read(),v=read(); ins(head,u,v); ins(back,v,u); } for(int i=1;i<=n;++i) Best[i]=s[i]=fa[i]=i; Pre(n);Tarjan(); for(int i=1;i<=dn;++i) val[id[i]]=val[d[id[i]]]+id[i]; for(int i=1;i<=n;++i) printf("%I64d%c",dfn[i]?val[i]:0,i==n?' ':' '); } return 0; }