HDU6350 Always Online

Always Online

给定 \(n\) 个点 \(m\) 条边的点仙人掌,求

\[ \sum_{1 ≤ s < t ≤ n} (s ⊕ t ⊕ \mathrm{maxflow}(s, t)) \]

其中 \(⊕\) 表示异或。

\(T ≤ 100,n ≤ 10^5,n − 1 ≤ m ≤ 1.5(n − 1),\sum n ≤ 10^6\)

题解

考虑点双缩点。

我们可以把每个环上最小的那条边断掉,把它的容量加到环的其他边上。显然如果路径要经过这个环,那么最小割会优先考虑这条边。

然后我们按照容量从大到小枚举每一条边,使用并查集维护连通块。每次加边的时候,这条边一定是它连接的两个连通块之间的最小割。那么直接记录每一个bit上面的01数量就行了。

还要用unsigned long long……不过ACM赛制还好。

因为有加法操作,所以要做到230。这个WrongAnswer看得我想把HDU的电脑砸了。

https://blog.csdn.net/V5ZSQ/article/details/82453394

CO int N=1e5+10;
struct edge {int u,v,w,f,next;}e[3*N]; // flag
int head[N],tot;
int vis[N],stk[N],top;
vector<int> nw;

IN bool cmp(int i,int j){
    return e[i].w>e[j].w;
}
IN void link(int u,int v,int w){
    e[++tot]=(edge){u,v,w,0,head[u]},head[u]=tot;
}
void dfs(int u,int ine){
    vis[u]=1;
    for(int i=head[u];i;i=e[i].next)if(!e[i].f){
        e[i].f=e[i^1].f=1;
        stk[++top]=i;
        if(!vis[e[i].v]){
            dfs(e[i].v,i);
            continue;
        }
        int mn=e[i].w;
        for(int j=top;j>=1;--j){
            mn=min(mn,e[stk[j]].w);
            e[stk[j]].f=2; // circle edge
            if(e[stk[j]].u==e[i].v) break;
        }
        bool flag=0; // delete only once
        while(1){
            int t=stk[top--];
            if(!flag and e[t].w==mn) flag=1;
            else{
                e[t].w+=mn;
                nw.push_back(t);
            }
            if(e[t].u==e[i].v) break;
        }
    }
    if(ine and e[ine].f!=2) nw.push_back(stk[top--]);
}

int fa[N],siz[N],num[N][31];

int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
void real_main(){
    int n=read<int>();
    fill(head+1,head+n+1,0),tot=1;
    for(int m=read<int>();m--;){
        int u=read<int>(),v=read<int>(),w=read<int>();
        link(u,v,w),link(v,u,w);
        assert(w<1e9);
    }
    fill(vis+1,vis+n+1,0);
    top=0,nw.clear();
    dfs(1,0);
    sort(nw.begin(),nw.end(),cmp);
    for(int i=1;i<=n;++i){
        fa[i]=i,siz[i]=1;
        for(int j=30;j>=0;--j) num[i][j]=i>>j&1;
    }
    uint64 ans=0;
    for(int i=0;i<(int)nw.size();++i){
        int u=e[nw[i]].u,v=e[nw[i]].v,w=e[nw[i]].w;
        // cerr<<"e= "<<u<<" "<<v<<" "<<w<<endl;
        u=find(u),v=find(v);
        for(int j=30;j>=0;--j){ // edit 1: 30 for operator+
            uint64 sum=0;
            if(w>>j&1){
                sum+=(uint64)num[u][j]*num[v][j];
                sum+=(uint64)(siz[u]-num[u][j])*(siz[v]-num[v][j]);
            }
            else{
                sum+=(uint64)num[u][j]*(siz[v]-num[v][j]);
                sum+=(uint64)(siz[u]-num[u][j])*num[v][j];
            }
            ans+=sum<<j;
        }
        for(int j=30;j>=0;--j) num[u][j]+=num[v][j];
        fa[v]=u,siz[u]+=siz[v];
    }
    printf("%llu\n",ans);
}
int main(){
    for(int T=read<int>();T--;) real_main();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/12203070.html