https://ac.nowcoder.com/acm/contest/883/A
題目大意,給出一個無向圖,設S(x)表示x的臨近點集合,臨近點即通過一條邊直接相連的點。有兩種操作,1是把邊集合(讀入順序)從l到r的邊狀態反轉(相連變斷開,斷開變相連),2是詢問兩個點的臨近集是否相等。
這裏首先涉及到的一個難點是如何表示臨近集,我們採用異或哈希的方式,首先爲所有點分配一個隨機權值,這裏用到了一個新版的隨機函數
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
for(int i=1;i<=n;i++)
val[i]=rng();
接下來我們把某個點的所有臨近點的權值異或起來,作爲它的臨近集表示。異或哈希的大概原理是,原始權值在每個bit上01的概率相等,而異或過程對於每個bit產生01的概率也是等價的,所以能保證隨機性。這個問題解決後,就能以O(1)時間判定臨近集的相等。
接下來考慮如何維護翻轉,一個簡單的分塊考慮是,對邊集分塊,最開始計算出每個塊對每個點的影響(add數組)(O(m^1.5)),然後對於1操作,標記塊的翻轉,零碎的邊直接計算出來;對於2操作,把所有翻轉的塊中對該點的影響都異或到現有答案上。這樣兩個操作的複雜度都是O(m^0.5),只可惜這樣會超時,由於是多組數據,而我們在每組數據中都要對add數組進行整體清零,這個複雜度是m^0.5*n的,三四組數據估計就不行了。
所以考慮縮減add數組,我們發現,如果一個點的度數比較小,那麼在計算它的臨近集時可以挨個枚舉相連的邊,複雜度是和度數成正比的。因而我們考慮,只把度數大於sqrt(m)的點用add數組維護,這樣的點總共不會超過2*sqrt(m)個。剩下的小點需要再維護一下零碎邊的翻轉性(e數組),同時塊的翻轉標記也會影響到它。
ps分塊題的細節真的一不留神就寫錯……不過思路直接還是挺容易debug的
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+2;
const int maxm=2e5+2;
#define ll long long
#define sz(x) (int)x.size()
ll val[maxn],s[maxn];
struct edge{
int u,v;
}es[maxm];
typedef pair<int,int> pir;
vector<pir> g[maxn];
bool big[maxn];
int cnt,mp[maxn];
ll add[500][1000];
int pos[maxm],tot;
int tag[500],e[maxm];
int n,m,q,t;
string ans;
int main(){
cin>>t;
while(t--){
cin>>n>>m;
int block=sqrt(m);
tot=0;
for(int i=1;i<=m;i++){
if((i-1)%block==0)
tot++;
pos[i]=tot;
}
for(int i=1;i<=n;i++)
g[i].clear();
for(int i=1;i<=m;i++){
cin>>es[i].u>>es[i].v;
g[es[i].u].push_back(pir(es[i].v,i));
g[es[i].v].push_back(pir(es[i].u,i));
}
memset(big,0,sizeof(big));
cnt=0;
for(int i=1;i<=n;i++)
if(sz(g[i])>block){
big[i]=1;
mp[i]=++cnt;
}
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
for(int i=1;i<=n;i++)
val[i]=rng();
for(int i=1;i<=tot;i++){
for(int j=1;j<=cnt;++j)
add[i][j]=0;
for(int j=(i-1)*block+1;j<=min(m,i*block);j++){
int u=es[j].u,v=es[j].v;
if(big[u])
add[i][mp[u]]^=val[v];
if(big[v])
add[i][mp[v]]^=val[u];
}
}
for(int i=1;i<=tot;i++)
tag[i]=1;
memset(s,0,sizeof(s));
memset(e,0,sizeof(e));
cin>>q;
ans="";
while(q--){
int opt;
cin>>opt;
if(opt==1){
int l,r;
cin>>l>>r;
int lp=pos[l],rp=pos[r];
if(lp<rp){
for(int i=lp+1;i<rp;i++)
tag[i]^=1;
for(int i=l;i<=block*lp;i++){
int u=es[i].u,v=es[i].v;
e[i]^=1;
if(big[u])
s[u]^=val[v];
if(big[v])
s[v]^=val[u];
}
for(int i=block*(rp-1)+1;i<=min(block*rp,r);i++){
int u=es[i].u,v=es[i].v;
e[i]^=1;
if(big[u])
s[u]^=val[v];
if(big[v])
s[v]^=val[u];
}
}else{
for(int i=l;i<=r;i++){
int u=es[i].u,v=es[i].v;
e[i]^=1;
if(big[u])
s[u]^=val[v];
if(big[v])
s[v]^=val[u];
}
}
}else{
int u,v;
cin>>u>>v;
ll valu=s[u],valv=s[v];
if(big[u]){
for(int i=1;i<=tot;i++){
if(tag[i])
valu^=add[i][mp[u]];
}
}else{
for(int i=0,id,to;i<sz(g[u]);i++){
id=g[u][i].second,to=g[u][i].first;
if(e[id]^tag[pos[id]]){
valu^=val[to];
}
}
}
if(big[v]){
for(int i=1;i<=tot;i++){
if(tag[i])
valv^=add[i][mp[v]];
}
}else{
for(int i=0,id,to;i<sz(g[v]);i++){
id=g[v][i].second,to=g[v][i].first;
if(e[id]^tag[pos[id]]){
valv^=val[to];
}
}
}
if(valu==valv){
ans+='1';
}else
ans+='0';
}
}
cout<<ans<<endl;
}
}
/*
1
5 4
1 2
1 3
4 2
4 3
3
2 1 4
1 1 1
2 1 2
*/