#4637. 樱符「完全墨染的樱花」

题目描述

暂无

数据范围

暂无

题解

考虑到这张图是仙人掌,即一条边最多属于一个简单环。

要求两点间的最大流,即求最小割,那要割的要么是桥边,要么是两条环边。

考虑到如果删去的是环边的话,那一定会删掉边权最小的边。

所以可以把每个环的最小的边删去,同时其他边加上这条边的权值,这样两点间的最小割不会改变,进而只要求一棵树的两点间的最小割即可。

所以用并查集维护即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5,P=998244353;
int n,m,p,t,hd[N],V[N],nx[N],W[N],f[N];
int a[N],b[N],fa[N],d[N],g[N],Q,ans,G;
bool vis[N];struct E{int u,v,w;}e[N],q[N];
bool cmp(E A,E B){return A.w>B.w;}
int get(int x){
    return f[x]==x?x:f[x]=get(f[x]);
}
int X(int x){return x>=P?x-P:x;}
int K(int x,int y){
    int z=1;
    for (;y;y>>=1,x=1ll*x*x%P)
        if (y&1) z=1ll*z*x%P;
    return z;
}
void add(int u,int v,int i){
    nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=i;
}
void dfs(int u,int fr){
    d[u]=d[fa[u]=fr]+1;
    for (int i=hd[u];i;i=nx[i])
        if (V[i]!=fr) g[V[i]]=W[i],dfs(V[i],u);
}
int main(){
    cin>>n>>m>>p;
    for (int i=1;i<=m;i++)
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    sort(e+1,e+m+1,cmp);
    for (int i=1;i<=n;i++) f[i]=i;
    for (int u,v,i=1;i<=m;i++){
        u=get(e[i].u);v=get(e[i].v);
        if (u==v) q[++Q]=e[i];
        else add(e[i].u,e[i].v,i),
            add(e[i].v,e[i].u,i),f[v]=u;
    }
    dfs(1,0);
    for (int u,v,i=1;i<=Q;i++){
        u=q[i].u;v=q[i].v;
        if (d[u]<d[v]) swap(u,v);
        while(d[u]>d[v]) e[g[u]].w+=q[i].w,u=fa[u];
        while(u!=v)
            e[g[u]].w+=q[i].w,u=fa[u],
            e[g[v]].w+=q[i].w,v=fa[v];
    }
    sort(e+1,e+m+1,cmp);
    for (int i=1;i<=n;i++)
        f[i]=i,a[i]=K(p,i),b[i]=K(p,1ll*(i-1)*n%(P-1));
    for (int u,v,i=1;i<=m;i++){
        u=get(e[i].u);v=get(e[i].v);if (u==v) continue;
        ans=X(ans+1ll*(1ll*a[u]*b[v]%P+1ll*a[v]*b[u]%P)*e[i].w%P);
        a[u]=X(a[u]+a[v]);b[u]=X(b[u]+b[v]);f[v]=u;
    }
    printf("%d
",ans);return 0;
}
原文地址:https://www.cnblogs.com/xjqxjq/p/11834913.html