F题启发式合并写法题解

先将不动点问题通过每个节点编号减一,就能转化成一个以i节点为根当前子树所有节点编号的mex()问题,通过将当前节点的所有子节点集合合并,就能得到当前节点为根集合,通过map维护其mex值,直接合并的时间复杂度最大为n^2(也就是形成一条链的形式),使用启发式合并后可将时间复杂度优化至nlogn,代码如下:

#include <iostream>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <stack>
#include <cstring>
#include <cmath>
#include <random>
// #define int long long 
#define ll long long 
#define ull unsigned long long 
#define pb push_back
#define yes cout << "Yes\n"
#define no cout << "No\n"
#define fi first
#define se second
#define rt return

using namespace std;

mt19937_64 rnd(time(0));
typedef pair<int,int> PII;
int dx[8] = {-1,0,1,0,-1,-1,1,1};
int dy[8] = {0,-1,0,1,-1,1,-1,1};

// const int M = 400010,P = 998244353;
// const int M = 400010,P = 1e9+7;
// int e[M],ne[M],h[M],idx;
// void add(int a,int b){e[idx] = b,ne[idx] = h[a],h[a] = idx++;}
// int lowbit(int x){return x&-x;}
// int lcm(int a,int b){return a*b/__gcd(a,b);}
// vector<vector<int>>a(n+1,vector<int>(m+1,0)); n*m的二维数组
// vector<vector<vector<int>>> dist(n+1,vector<vector<int>>(m+1,vector<int>(k+1, INF))); n*m*k的三维数组

const int N = 500010;
map<int,int>mp[N];
vector<int>p[N];
int f[N];
int cnt[N];
int t[N];
ll res = 0;

void merge(int &x,int &y){
	
	if(x == y) return;
	if(cnt[x] > cnt[y]) swap(x,y);
	for(auto xx:mp[x])
		mp[y][xx.fi]++;
	while(mp[y].count(f[y])) f[y]++;
	cnt[y]+=cnt[x];
	cnt[x] = 0;
	mp[x].clear();
	
}

void dfs(int u,int fa){
	
	int j = -1;
	for(auto xx:p[u]){
		if(xx == fa) continue;
		dfs(xx,u);
		if(j == -1 || cnt[t[xx]] > cnt[t[j]]) j = xx;
 	}
 	if(j!=-1) swap(t[j],t[u]);
 	for(auto xx:p[u]){
 		if(xx == fa) continue;
 		merge(t[xx],t[u]);
 	}
 	while(mp[t[u]].count(f[t[u]])) f[t[u]]++;
	res+=f[t[u]];
}

void slove(){
	    
 	int n;
 	cin >> n;
 	for(int i=0;i<=n;i++) cnt[i] = 1,mp[i][i] = 1;
 	for(int i=0;i<=n;i++) t[i] = i;
 	for(int i=1;i<n;i++){
 		int u,v;
 		cin >> u >> v;
        u--,v--;
 		p[u].push_back(v);
 		p[v].push_back(u);
 	}
 	dfs(n-1,-1);
 	cout << res << '\n';
 	
}
 
signed main(){
	
	std::ios::sync_with_stdio(0);
  	std::cin.tie(0);
	int t = 1;
	//多组
	// cin >> t;
	while(t--) slove();
	
	//养成好习惯^_^
	return 0;
	
}