P4630 [APIO2018] Duathlon 鐵人兩項
阿新 • • 發佈:2018-11-30
完全沒想到圓方樹orz……
我們先考慮建出這個圖的圓方樹,如果把方點的權值設為這個點雙的大小,圓點的權值為\(-1\),那麼起點\(s\)終點\(f\)的方案數就是這條路徑上的權值總和,這樣的話就可以做到\(O(n^2)\)
然後考慮用dfs優化,即計算每個點被經過了幾次,那麼就可以做到\(O(n)\)了
順便注意起點和終點互換屬於不同的方案,所以不要漏了
//minamoto #include<bits/stdc++.h> #define ll long long #define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i) #define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i) #define go(G,u) for(register int i=G.head[u],v=G.e[i].v;i;i=G.e[i].nx,v=G.e[i].v) using namespace std; inline int max(const int &x,const int &y){return x>y?x:y;} inline int min(const int &x,const int &y){return x<y?x:y;} char buf[1<<21],*p1=buf,*p2=buf; inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;} int read(){ int res,f=1;char ch; while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1); for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0'); return res*f; } const int N=5e5+5; struct eg{int v,nx;}; struct Gr{ eg e[N];int head[N],tot; inline void add(int u,int v){e[++tot]={v,head[u]},head[u]=tot;} }G,T; int n,m,tim,tot,x,u,v,top,dfn[N],low[N],st[N],val[N],sz[N],sum;ll ans; void tarjan(int u){ dfn[u]=low[u]=++tim,st[++top]=u; sz[u]=1,val[u]=-1; go(G,u)if(dfn[v])low[u]=min(low[u],dfn[v]); else{ tarjan(v),low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ T.add(u,++tot),val[tot]=1; do{ x=st[top--],T.add(tot,x); sz[tot]+=sz[x],++val[tot]; }while(x!=v); sz[u]+=sz[tot]; } } } void dfs(int u){ if(u<=n)ans+=1ll*(sum-1)*val[u]; ans+=1ll*(sum-sz[u])*sz[u]*val[u]; go(T,u)ans+=1ll*(sum-sz[v])*sz[v]*val[u],dfs(v); } int main(){ // freopen("testdata.in","r",stdin); tot=n=read(),m=read(); while(m--)u=read(),v=read(),G.add(u,v),G.add(v,u); fp(i,1,n)if(!dfn[i])tarjan(i),sum=sz[i],dfs(i); printf("%lld\n",ans);return 0; }