并查集

使用并查集维护维护当前状态下联通块的大小。

建图的时候只考虑会有影响的边。

  • 每个联通块的收益值是
  • 首先计算当前状态下的收益值
  • 对于每个可能占领的城市,我们计算收益的增加值

遍历每个可能的连通块

初始的时候 我们新的城市算一个联通块大小为1,假设遍历的第一个连通块大小为

则此时增量为

依此类推

#include <iostream>
#include <vector>
using namespace std;
const int maxn = 1e5 +10;
vector<int> f(maxn);
vector<int> v(maxn);
vector<vector<int>> g(maxn);
vector<int> vis(maxn,0);
int find(int p ){
    if (f[p] == p) 
        return p;
    return f[p] = find(f[p]);
}
void merge(int x,int y){
    int p = find(x);
    int q = find(y);
    if(p != q){
        f[p] = q;
        v[q] = v[p] + v[q];
    }
}
void zero(int n){
    for (int i = 0 ; i < n + 1 ; i++) vis[i] = 0;
}
int main() {
    int n,m;
    string s;
    cin >> n >> m  >> s;
    for (int i = 0 ; i < n + 1 ; i++) v[i] = 1,f[i] = i;//初始化并查集状态
    long long ans = 0;
    int re = -1;
    int tmp = 0;
    while(m--){
        int x,y;
        cin >> x >> y;
        if(s[x - 1] == '1' && s[y - 1] == '1'){
            merge(x,y);
        }
        else if(s[x - 1] == '0' && s[y - 1] == '1'){
            g[x].push_back(y);
        }
        else if(s[x - 1] == '1' && s[y - 1] == '0'){
            g[y].push_back(x);
        }
    }
    //计算原有的收益
    for (int i = 0 ; i < n ; i++){
        if(s[i] == '1'){
            int k = find(i+1);
            if(vis[k] == 0){
                int sum = v[k];
                ans += 1ll * sum * (sum - 1) / 2;
                vis[k] = 1;
            }
            
        }
        if(re == -1 && s[i] == '0'){//如果占领的每个城市都与原来占领的城市没有连接,会出错
            re = i + 1;
        }
    }
    zero(n);
    for (int i = 0 ; i < n ; i++)   {
      //计算新***市的收益
        if(s[i] == '0' && g[i+1].size() > 0){
            long long  key = 1;
            long long tmpp = 0;
            for (auto &zz : g[i+1]){
                // cout << zz <<" " <<i+1 << find(zz) <<endl;
                int zzz = find(zz);
                if(vis[zzz] == 0){
                    tmpp += (key + v[zzz]) * (key + v[zzz] - 1) / 2 -  key * (key - 1) / 2 -  v[zzz] * (v[zzz] - 1) / 2;
                    // cout << tmpp << endl;
                    key += v[zzz];
                    vis[zzz] = 1;
                }
            
            }
            // tmpp += key;
            if( tmp < tmpp){
                tmp = tmpp;
                re = i + 1;
            }
            zero(n);
        }
    }
    // cout << ans << endl;
    cout << re << " " << ans + tmp << endl;

    return 0;
}
// 64 位输出请用 printf("%lld")