1. 程式人生 > >P4630 [APIO2018] Duathlon 鐵人兩項

P4630 [APIO2018] Duathlon 鐵人兩項

傳送門

完全沒想到圓方樹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;
}