[BZOJ5463][APIO2018]铁人两项:Tarjan+圆方树

分析

根据题目中的要求,从\(s\)出发前往\(f\)一定可以,并且只可能经过这两个结点所在的点双连通分量和它们之间的点双连通分量,因此切换点\(c\)只能从这些点中选取。

建出圆方树后,因为圆方树上一条路径的两个端点(圆点)不能作为切换点,并且路径上其他的圆点都被两个路径上的点双连通分量所包含,可以发现,如果把方点的权值设为这个点双连通分量的\(siz\),圆点的权值设为\(-1\),那么圆方树上所有两个端点都是圆点的路径的权值和就是答案。这个问题可以通过考虑每个结点在多少条路径上来解决。

时间复杂度为\(O(n)\)

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void write(LL x){
    if(x/10) write(x/10);
    putchar(x%10+'0');
}

void writeln(LL x){
    write(x);
    putchar('\n');
}

const int MAXN=1e6+5;
const int MAXM=2e6+5;

int n,m,ecnt,head[MAXN<<1];
int dfn[MAXN],low[MAXN],tot;
int sta[MAXN],top;
int cnt;
int totsiz,siz[MAXN<<1];
LL ans;
bool vis[MAXN<<1];
std::vector<int> vdcc[MAXN];

struct Edge{
    int to,nxt;
}e[MAXM<<2];

inline void add_edge(int bg,int ed){
    ++ecnt;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    head[bg]=ecnt;
}

void tarjan(int x){
    dfn[x]=low[x]=++tot;
    sta[++top]=x;
    trav(i,x){
        int ver=e[i].to;
        if(!dfn[ver]){
            tarjan(ver);
            low[x]=std::min(low[x],low[ver]);
            if(low[ver]>=dfn[x]){
                ++cnt;int las=0;
                vdcc[cnt].push_back(x);
                while(las!=ver){
                    las=sta[top];
                    vdcc[cnt].push_back(sta[top]);
                    --top;
                }
            }
        }
        else low[x]=std::min(low[x],dfn[ver]);
    }
}

int getsiz(int x,int pre){
    int ret=0;
    trav(i,x){
        int ver=e[i].to;
        if(ver==pre) continue;
        ret+=getsiz(ver,x);
    }
    if(x<=n) ++ret;
    return ret;
}

void dfs(int x,int pre){
    vis[x]=true;
    trav(i,x){
        int ver=e[i].to;
        if(ver==pre) continue;
        dfs(ver,x);
        if(x<=n) ans-=1ll*siz[ver]*siz[x]*2;
        else ans+=1ll*siz[ver]*siz[x]*(LL)vdcc[x-n].size()*2;
        siz[x]+=siz[ver];
    }
    if(x<=n) ans-=1ll*(siz[x]+1)*(totsiz-siz[x])*2-2,++siz[x];
    else ans+=1ll*siz[x]*(totsiz-siz[x])*(LL)vdcc[x-n].size()*2;
}

int main(){
    n=read(),m=read();
    rin(i,1,m){
        int u=read(),v=read();
        add_edge(u,v);
        add_edge(v,u);
    }
    rin(i,1,n){
        if(dfn[i]) continue;
        if(!head[i]){
            vdcc[++cnt].push_back(i);
            continue;
        }
        top=0;tarjan(i);
    }
    ecnt=0;memset(head,0,sizeof head);
    rin(i,1,cnt){
        rin(j,0,(int)vdcc[i].size()-1){
            add_edge(n+i,vdcc[i][j]);
            add_edge(vdcc[i][j],n+i);
        }
    }
    rin(i,1,n){
        if(vis[i]) continue;
        totsiz=getsiz(i,0);
        dfs(i,0);
    }
    writeln(ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/10371972.html
今日推荐