题解:CF1996G Penacony
题意
给定一个环,环上有 \(n\) 对点,用最少的边数使每对点都联通。
思路
在环上,两点之间有两种连接方式(如果看做圆上两点,分别是优弧和劣弧)。翻转这个连接(从一个弧至对面的弧)就相当于将其异或,因为异或有如下性质:
\[a \oplus a = 0
\]
所以,对于每一对点,我们对其分别分配一个相同但与其他点对不同的值,并处理异或前缀和,前缀和相同的边就代表拥有同样的连边情况,就可以同时翻转至对面。由于异或值容易重复,例如 \(1 \oplus 2 = 5 \oplus 6\),\(1 \oplus 2 \oplus 3 = 4 \oplus 8 \oplus 12\)。所以我们用随机哈希来避免冲突。保险起见,再加上双哈希。
最终,策略就是删去相同的哈希值的边,我们枚举每个哈希值,统计最多的数量。
代码
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
int T;
int n,m;
int ans;
pair<int,int> a[200010];
map<pair<int,int>,int>mp;
signed main(){
srand(time(0));
cin>>T;
while(T--){
memset(a,0,sizeof(a));
mp.clear();
ans=0;
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
int r1=666ull*rand()*rand()*rand()+rand()*rand()+rand();//随机生成哈希值
int r2=114514ull*rand()*rand()*rand()+1919810ull*rand()*rand()+360ull*rand();
a[x].first^=r1;a[y].first^=r1;
a[x].second^=r2;a[y].second^=r2;//对两关联点分配同样的哈希值
}
for(int i=1;i<=n;i++){
a[i].first^=a[i-1].first;
a[i].second^=a[i-1].second;//前缀和
mp[a[i]]++;//统计一样的哈希值最多的边
}
for(map<pair<int,int>,int>::iterator it=mp.begin();it!=mp.end();it++){
ans=max(ans,it->second);
}
cout<<n-ans<<endl;
}
}