HDU6431 NewNippori

NewNippori

给定 \(n\) 个点 \(m\) 条边的无向图,求

\[ \sum_{i=1}^n\sum_{j=1}^{i-1} \min\{\mathrm{maxflow}(i, j), 3\} \]

\(\sum n ≤ 4 × 10^5,\sum m ≤ 7 × 10^5\)

题解

最大流显然要转化成最小割来考虑。

2018 Multi-University Training Contest 10 solutions BY 雅礼中学

首先题目就是要你求每对点之间的最大流对 \(3\)\(\min\) 的和。也就是要判断

  • 有多少对点满足存在一种方案,删去一条边后不连通。

  • 有多少对点满足存在一种方案,删去两条边后不连通。

第一种很好算,找出边双连通分量即可。不妨假设现在整张图是边双连通的。

先搞出DFS生成树来, 容易发现删去的两条边可能是:

  • 某一条边是树边,另一条边是非树边:要求这条树边仅被这条非树边覆盖

  • 两条边都是树边:要求这两条树边被覆盖情况相同。不妨考虑类似 共价大爷游长沙 的思路,即给每条非树边随机一个 \(10^{18}\) 以内的权值,一个树边的权值为覆盖它的非树边的权值的异或和。然后用上 map 就很好统计了,时间复杂度 \(O(n \log m)\)

是的,好统计。口胡谁TM都会。

做出生成树之后怎么办呢?考虑再进行一次“边三连通分量”划分,把相互之间最小割 \(\geq 3\) 的分在同一个连通块中。

  • 树边+非树边就能割开的组合:把生成树上的这条树边断掉。

  • 都是树边的组合:有些复杂。因为无向图的DFS树只有返祖边,所以这样的树边一定是连续的一段。

考虑 \(x \leftrightarrow y \leftrightarrow z \leftrightarrow w\) 这一条链,其中 \((x,y),(y,z),(z,w)\) 被覆盖的情况相同,并且非树边数量要 \(\geq 2\),不然就是上面那个树边+非树边组合了。

在这种情况下,\(y,z\) 子树中的非树边一定逃不出它们的子树内部,那么 \(y,z\) 子树显然可以各自形成一个边三连通块,直接把 \((x,y),(y,z),(z,w)\) 这三条边断开就行了。

但是 \(x,w\) 子树内的点却可能形成 \(\geq 3\) 的最小割,这两棵子树可能在同一边三连通分量中。怎么办呢?再在 \(x,w\) 之间连一条边就行了。

为了做这题,我又写了对拍和造数据。

IN int64 rd(){
    return rand()^rand()<<15^(int64)rand()<<30^(int64)rand()<<45^(int64)rand()<<60;
}

CO int N=1e5+10,M=3e5+10;
struct edge {int v,next;}e[2*M];
int head[N],tot;

IN void link(int u,int v){
    e[++tot]=(edge){v,head[u]},head[u]=tot;
    e[++tot]=(edge){u,head[v]},head[v]=tot;
}

int pos[N],low[N],dfn;
int stk[N],top;
int col[N],idx;
vector<int> ecc[N];

void tarjan(int u,int ine){
    pos[u]=low[u]=++dfn;
    stk[++top]=u;
    for(int i=head[u];i;i=e[i].next)if(i!=(ine^1)){
        if(!pos[e[i].v]){
            tarjan(e[i].v,i);
            low[u]=min(low[u],low[e[i].v]);
        }
        else low[u]=min(low[u],pos[e[i].v]);
    }
    if(low[u]==pos[u]){
        ecc[++idx].clear();
        do{
            int x=stk[top];
            col[x]=idx,ecc[idx].push_back(x);
        }while(stk[top--]!=u);
    }
}

namespace T{
    int n,ref[N];
    edge e[4*M];
    int head[N],tot;

    IN void init(){
        fill(head+1,head+n+1,0),tot=1;
    }
    IN void link(int u,int v){
        e[++tot]=(edge){v,head[u]},head[u]=tot;
        e[++tot]=(edge){u,head[v]},head[v]=tot;
    }

    int pos[N],dfn;
    int64 cnt[N];
    map<int64,int> tree;
    set<int64> non;
    map<int64,pair<int,int> > add;
    int tag[2*M];

    void build(int u,int ine){
        pos[u]=++dfn,cnt[u]=0;
        for(int i=head[u];i;i=e[i].next)if(i!=(ine^1)){
            if(!pos[e[i].v]){
                tag[i/2]=1;
                // cerr<<"tree "<<i/2<<endl;
                build(e[i].v,i);
                cnt[u]^=cnt[e[i].v];
            }
            else if(pos[e[i].v]<pos[u]){
                int64 x=rd();
                // cerr<<"rand "<<i/2<<" "<<x<<endl;
                cnt[u]^=x,cnt[e[i].v]^=x;
                non.insert(x);
            }
        }
        // cerr<<"rand "<<ine/2<<" "<<cnt[u]<<endl;
        if(non.count(cnt[u])){
            tag[ine/2]=0;
            // cerr<<"non "<<ine/2<<endl;
            return; // edit 1
        }
        if(tree.count(cnt[u])){
            tag[ine/2]=0,tag[tree[cnt[u]]/2]=0;
            // cerr<<"tr "<<ine/2<<" "<<tree[cnt[u]]/2<<endl;
        }
        tree[cnt[u]]=ine;
        if(!add.count(cnt[u])) add[cnt[u]]=make_pair(u,0);
        else add[cnt[u]].second=e[ine^1].v;
    }

    int vis[N];

    int64 main(){
        // cerr<<"edge m="<<tot/2<<endl;
        // for(int i=1;i<=tot/2;++i){
        //  int u=e[2*i].v,v=e[2*i+1].v;
        //  cerr<<i<<" "<<u<<" "<<v<<endl;
        // }
        non.clear(),tree.clear();
        add.clear();
        fill(pos+1,pos+n+1,0),dfn=0;
        fill(tag+1,tag+tot+1,0);
        build(1,0);
        for(map<int64,pair<int,int> >::iterator i=add.begin();i!=add.end();++i)
            if(i->second.first and i->second.second){
                link(i->second.first,i->second.second);
                // cerr<<"link "<<i->second.first<<" "<<i->second.second<<endl;
                tag[tot/2]=1;
            }
        // cerr<<"tag="<<endl;
        // for(int i=1;i<=tot/2;++i) cerr<<i<<" tag="<<tag[i]<<endl;
        // cerr<<endl;
        fill(vis+1,vis+n+1,0);
        int64 ans=0;
        for(int i=1;i<=n;++i)if(!vis[i]){
            int siz=0;
            deque<int> Q(1,i);
            while(Q.size()){
                int u=Q.front();Q.pop_front();
                vis[u]=1,++siz;
                for(int i=head[u];i;i=e[i].next)if(tag[i/2])
                    if(!vis[e[i].v]) Q.push_back(e[i].v);
            }
            ans+=(int64)siz*(siz-1)/2;
        }
        // cerr<<"ans="<<3*ans+2*((int64)n*(n-1)/2-ans)<<endl;
        return 3*ans+2*((int64)n*(n-1)/2-ans);
    }
}

int vis[2*M];

int64 solve(int x){
    idx=0,tarjan(x,0);
    int64 ans=0;
    int n=0;
    for(int i=1;i<=idx;++i) n+=ecc[i].size();
    for(int i=1;i<=idx;++i) ans+=(int64)ecc[i].size()*(n-ecc[i].size());
    ans/=2;
    for(int i=1;i<=idx;++i){
        // reverse(ecc[i].begin(),ecc[i].end());
        // cerr<<"ecc=";
        // for(int j=0;j<(int)ecc[i].size();++j) cerr<<" "<<ecc[i][j];
        // cerr<<endl;
        T::n=0;
        for(int j=0;j<(int)ecc[i].size();++j) T::ref[ecc[i][j]]=++T::n;
        T::init();
        for(int j=0;j<(int)ecc[i].size();++j){
            int u=ecc[i][j];
            for(int k=head[u];k;k=e[k].next)if(!vis[k]){
                int v=e[k].v;
                if(col[v]!=i) continue;
                T::link(T::ref[u],T::ref[v]);
                vis[k]=vis[k^1]=1;
            }
        }
        ans+=T::main();
    }
    return ans;
}
void real_main(){
    int n=read<int>(),m=read<int>();
    fill(head+1,head+n+1,0),tot=1;
    for(int i=1;i<=m;++i) link(read<int>(),read<int>());
    fill(pos+1,pos+n+1,0),dfn=0;
    fill(vis+1,vis+2*m+1,0);
    int64 ans=0;
    for(int i=1;i<=n;++i)if(!pos[i]) ans+=solve(i);
    printf("%lld\n",ans);
}
int main(){
    // freopen("gen.in","r",stdin),freopen("mine.out","w",stdout);
    srand(20030506);
    for(int T=read<int>();T--;) real_main();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/12201507.html
hdu