Loading

题解-CF1444C Team-Building

题面

CF1444C Team-Building

\(n\) 个点 \(m\) 条边,每个点有颜色 \(c_i(1\le c_i\le k)\),求有多少个颜色对两组点并后是二分图。

数据范围:\(1\le n,m,k\le 5\cdot 10^5\)


蒟蒻语

听人说是可撤销并查集后弃疗了,打出正解了没打完,其实普通并查集(权值并查集套权值并查集)就够了。

因为前面好多法师塔,只下了 \(7\) 分。


题解

看到判断二分图想到黑白染色。但是不想 dfs,怎么办?

有两种办法:拆点并查集和路径带权值并查集。蒟蒻选了后者。

对于一个颜色,内部不是二分图那么加入了另一组也不是二分图了。

所以可以先用路径带权值并查集在每个颜色内部把黑白关系连好,内部不是二分图的以后也不用管了。

然后处理两个颜色之间的。虽然看似 \(\Theta(n^2)\),但是如果两个颜色间没边必然是二分图,否则只有 \(\Theta(m)\) 个。

处理两个颜色间的,可以把两个颜色内部的每个连通块看作一个点,再套一层权值并查集然后连黑白关系。

外面那层并查集每次只需要初始化要用的就可以了,所以不需要可撤销。


代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair((a),(b))
#define x first
#define y second
#define bg begin()
#define ed end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
#define R(i,a,b) for(int i=(a),i##E=(b);i<i##E;i++)
#define L(i,a,b) for(int i=(b)-1,i##E=(a)-1;i>i##E;i--)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=5e5;
int n,m,k,c[N],good;
bool bad[N];

//Graph
map<pair<int,int>,int> mp;
int mc; pair<int,int> me[N];
vector<pair<int,int>> e[N];

//Dsu
struct dsu{
    int te[N],dep[N];
    int find(int u){
        if(te[u]==u) return u;
        int an=find(te[u]);
        dep[u]^=dep[te[u]];
        return te[u]=an;
    }
}d[2];

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>k,good=k;
    R(i,0,n) cin>>c[i],--c[i];
    R(i,0,m){
        int u,v; cin>>u>>v,--u,--v;
        if(c[u]>c[v]) swap(u,v);
        pair<int,int> t=mp(c[u],c[v]);
        if(!mp.count(t)) mp[t]=mc,me[mc]=t,mc++;
        e[mp[t]].pb(mp(u,v));
    }
    iota(d[0].te,d[0].te+n,0);
    R(i,0,k)if(mp.count(mp(i,i))){
        for(pair<int,int> t:e[mp[mp(i,i)]]){
            int x=d[0].find(t.x),y=d[0].find(t.y);
            if(x==y&&(d[0].dep[t.x]^d[0].dep[t.y]^1)!=0){bad[i]=true,good--;break;}
            d[0].dep[x]=d[0].dep[t.x]^d[0].dep[t.y]^1,d[0].te[x]=y;
        }
    }
    // cout<<"good="<<good<<'\n';
    ll ans=1ll*good*(good-1)/2;
    R(i,0,mc)if(me[i].x!=me[i].y){
        if(bad[me[i].x]||bad[me[i].y]) continue;
        for(pair<int,int> t:e[i]){
            int xi=d[0].find(t.x),yi=d[0].find(t.y);
            d[1].dep[xi]=0,d[1].te[xi]=xi,d[1].dep[yi]=0,d[1].te[yi]=yi;
        }
        for(pair<int,int> t:e[i]){
            int xi=d[0].find(t.x),yi=d[0].find(t.y);
            int x=d[1].find(xi),y=d[1].find(yi);
            if(x==y&&(d[1].dep[xi]^d[0].dep[t.x]
                ^d[1].dep[yi]^d[0].dep[t.y]^1)!=0){ans--;break;}
            d[1].dep[x]=d[1].dep[xi]^d[0].dep[t.x]
                ^d[1].dep[yi]^d[0].dep[t.y]^1,d[1].te[x]=y;
        }
    }    
    cout<<ans<<'\n';
    return 0;
}

祝大家学习愉快!

posted @ 2020-11-02 08:29  George1123  阅读(369)  评论(0编辑  收藏  举报