zoukankan      html  css  js  c++  java
  • 题解[CF1444C Team-Building]

    题目描述

    给定一张 (n) 个节点 (m) 条边的无向图,没有自环重边。

    每个节点都在一个组中,共有 (k) 组,可能存在某组为空。

    求选出两组点,使它们能构成二分图的方案数。

    (n,m,k<=5×10^5)

    Sol

    二分图是图中没有奇数环的图。我们现在每个点集内部找奇环,删掉有奇环的点集。

    然后在没有奇环的点集中两两配对,枚举跨越点集的边,如果不合法,答案减一。

    这个过程看上去简单,但实现却有点困难。代码巧妙(尤其是连边的方式),码的时候要细心。

    上代码。

    连边

    map<pair<int,int>,int>mp;//映射关系:点集与点集之间的边-->序号
    pair<int,int>p[N];//用来存边(点集与点集之间)
    vector< pair<int,int> >e[N];//用来存边(点与点之间)
    for(int i=1;i<=m;i++){
    	int u=read(),v=read();
    	if(bl[u]>bl[v]) swap(u,v);
    	pair<int,int> nw=make_pair(bl[u],bl[v]);
    	if(!mp.count(nw)) mp[nw]=++cnt,p[cnt]=nw;
    	e[mp[nw]].push_back(make_pair(u,v));
      //在点集bl[u]与点集bl[v]之间的边,在一个边集e[mp[nw]]中
    }
    

    找点集内部奇数环

    for(int i=1;i<=k;i++){
    	pair<int,int> now=make_pair(i,i);
      //看第i个点集内部有没有边
    	if(!mp.count(now)) continue;
    	for(int j=0;j<(int)e[mp[now]].size();j++){
    		int x=e[mp[now]][j].first,y=e[mp[now]][j].second;
          //枚举边
    		int nx=mfind1(x),ny=mfind1(y);
          //有奇环
    		if(nx==ny&&(d1[x]^d1[y]^1)){bad[i]=1;continue;}
          //x是要成为y的儿子的,如果x到y的祖先的路上(这是一个返祖过程)要经过奇数条边,就说明存在奇环
    		d1[nx]=d1[x]^d1[y]^1,f1[nx]=ny;
          //合并,更新距离
    	}
    }
    

    点集之间的不合法状态

    for(int i=1;i<=cnt;i++){
    	int xx=p[i].first,yy=p[i].second;
    	if(xx==yy||bad[xx]||bad[yy]) continue;
      //自己到自己||奇环点集-->跳过
    	for(int j=0;j<(int)e[i].size();j++){			
    		int x=e[i][j].first,y=e[i][j].second;
    		int nx=mfind1(x),ny=mfind1(y);
    		d2[nx]=0,d2[ny]=0,f2[nx]=nx,f2[ny]=ny;
          //初始化
    	}
    	for(int j=0;j<(int)e[i].size();j++){
    		int x=e[i][j].first,y=e[i][j].second;
    		int nx=mfind1(x),ny=mfind1(y);
    		int fx=mfind2(nx),fy=mfind2(ny);
    		if(fx==fy&&(d2[nx]^d1[x]^d2[ny]^d1[y]^1)){ans--;break;}
    		d2[fx]=(d2[nx]^d1[x]^d2[ny]^d1[y]^1),f2[fx]=fy;
          //同理,判断奇环
    	}
    }
    

    注意

    (d1,f1)为第一个带权并查集

    (d2,f2)是临时的,合并两个点集的并查集,每次都要更新

    Code

    #include<bits/stdc++.h>
    #define N (500010)
    #define ll long long
    using namespace std;
    int n,m,k,cnt,tot;
    int bl[N],f1[N],f2[N];
    bool d1[N],d2[N],bad[N];
    map<pair<int,int>,int>mp;
    pair<int,int>p[N];
    vector< pair<int,int> >e[N];
    inline int read(){
    	int w=0;
    	char ch=getchar();
    	while(ch>'9'||ch<'0') ch=getchar();
    	while(ch>='0'&&ch<='9'){
    		w=(w<<3)+(w<<1)+(ch^48);
    		ch=getchar();
    	}
    	return w;
    }
    inline int mfind1(int x){
    	if(x!=f1[x]){
    		int rt=mfind1(f1[x]);
    		d1[x]^=d1[f1[x]];
    		return f1[x]=rt;
    	}
    	return x;
    }
    inline int mfind2(int x){
    	if(f2[x]!=x){
    		int rt=mfind2(f2[x]);
    		d2[x]^=d2[f2[x]];
    		return f2[x]=rt;
    	}
    	return x;
    }
    int main(){
    	n=read(),m=read(),k=read();
    	for(int i=1;i<=n;i++) bl[i]=read(),f1[i]=i,d1[i]=0;
    	for(int i=1;i<=m;i++){
    		int u=read(),v=read();
    		if(bl[u]>bl[v]) swap(u,v);
    		pair<int,int> nw=make_pair(bl[u],bl[v]);
    		if(!mp.count(nw)) mp[nw]=++cnt,p[cnt]=nw;
    		e[mp[nw]].push_back(make_pair(u,v));
    	}
    	for(int i=1;i<=k;i++){
    		pair<int,int> now=make_pair(i,i);
    		if(!mp.count(now)) continue;
    		for(int j=0;j<(int)e[mp[now]].size();j++){
    			int x=e[mp[now]][j].first,y=e[mp[now]][j].second;
    			int nx=mfind1(x),ny=mfind1(y);
    			if(nx==ny&&(d1[x]^d1[y]^1)){bad[i]=1;continue;}
    			d1[nx]=d1[x]^d1[y]^1,f1[nx]=ny;
    		}
    	}
    //	for(int i=1;i<=n;i++) cout<<f1[i]<<" ";
    //	puts("");
    	for(int i=1;i<=k;i++)
    		if(!bad[i]) tot++;
    //	cout<<"tot="<<tot<<endl;
    //	cout<<"cnt="<<cnt<<endl;
    //	for(int i=1;i<=n;i++) cout<<d2[i]<<" ";
    //	puts("");
    	ll ans=(ll)tot*(tot-1)/2;
    	for(int i=1;i<=cnt;i++){
    		int xx=p[i].first,yy=p[i].second;
    		if(xx==yy||bad[xx]||bad[yy]) continue;
    		for(int j=0;j<(int)e[i].size();j++){			
    			int x=e[i][j].first,y=e[i][j].second;
    			int nx=mfind1(x),ny=mfind1(y);
    			d2[nx]=0,d2[ny]=0,f2[nx]=nx,f2[ny]=ny;
    		}
    //		for(int j=1;j<=n;j++) cout<<d2[j]<<" ";
    //		puts("");
    		for(int j=0;j<(int)e[i].size();j++){
    			int x=e[i][j].first,y=e[i][j].second;
    			int nx=mfind1(x),ny=mfind1(y);
    			int fx=mfind2(nx),fy=mfind2(ny);
    			if(fx==fy&&(d2[nx]^d1[x]^d2[ny]^d1[y]^1)){ans--;break;}
    			d2[fx]=(d2[nx]^d1[x]^d2[ny]^d1[y]^1),f2[fx]=fy;
    		}
    //		for(int j=1;j<=n;j++) cout<<d2[j]<<" ";
    //		puts("");
    //		cout<<"ans="<<ans<<endl;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    ps:打开注释可以看过程的

    完结撒花❀

  • 相关阅读:
    pinyin4j 实现 中文和拼音之间转化
    关于Exceptionless日志收集框架如何关闭磁盘缓存
    WPF桌面程序在请求接口时如何防止被常用的抓包软件Fiddler抓包
    Docker可视化容器Portainer
    记我第一次玩Docker
    最火热的极速开发框架Spring Boot
    MySQL开发规范
    5分钟入门AWK
    必须掌握的30种SQL语句优化
    支付接口的幂等性设计
  • 原文地址:https://www.cnblogs.com/xxbbkk/p/14453264.html
Copyright © 2011-2022 走看看