【loj 3274】「JOISC 2020 Day2」變色龍之戀【分治】
阿新 • • 發佈:2021-01-09
Solution
對於任意兩個點\(x,y\),如果它們同時參加會議,得到的顏色只有\(1\)種,僅有\(3\)種情況:
\(1.\)\(x,y\)顏色相同
\(2.\)\(x\)喜歡\(y\),\(y\)不喜歡\(x\)
\(3.\)\(y\)喜歡\(x\),\(x\)不喜歡\(y\)
在這樣的情況下我們對\(x,y\)連邊,顯然,每個點的度數都是\(1\)或\(3\)
如果點\(x\)的度數是\(1\),那麼這個點\(y\)一定是與\(x\)顏色相同的點,直接得到答案
否則,考慮依次將\(x\)與它所連的三個點中任選\(2\)個進行詢問,如果找到的是喜歡\(x\)的點與和\(x\)
據此,我們就能確定所有\(2,3\)型別的邊,那麼剩下的就是我們需要的\(1\)號型別的邊。
因此,如果我們找到了所有的邊,就能用約\(6n\)次詢問得出答案。
考慮如何快速找邊:
首先,我們考慮在原序列中找到一個極大獨立集:這顯然可以通過從左至右掃一遍,依次加入當前的獨立集\(S\)進行詢問,如果答案是\(|S|+1\),那麼\(S\)與當前點之間沒有邊,可已加入。
找到獨立集後,對於獨立集外的點,我們在獨立集中二分找到它們之間的邊。
現在,我們就剩下剩餘部分內部的邊了,遞迴處理即可。
因為每個點度數\(\le 3\),所以獨立集大小\(\ge\)總點數的\(\frac 14\),所以每次將點集的規模縮小到原來的\(\frac 34\),因此總共找獨立集的迴圈數計算一下極限情況也不超過\(10n\),而找到每一條邊都需要\(log(n)\)次查詢,一共也大約只需要\(3nlog(n)\)次查詢。
因此複雜度是正確的,可以通過此題。
Code
#include "chameleon.h" #include <bits/stdc++.h> using namespace std; const int N=1010; vector<int> p[N]; int vis[N],lov[N],whlov[N]; inline int ask(int a,int b,int c){ vector<int> p;p.push_back(a);p.push_back(b);p.push_back(c); return Query(p); } inline int ask(vector<int> a,int b){ a.push_back(b); return Query(a)==a.size(); } inline int findans(int x,vector<int> v){ if(v.size()==1){ p[x].push_back(v[0]),p[v[0]].push_back(x); return v[0]; } int mid=v.size()>>1; vector<int> L,R; for(int i=0;i<v.size();++i){ if(i<mid) L.push_back(v[i]); else R.push_back(v[i]); } if(ask(L,x)) return findans(x,R); else return findans(x,L); } void Solve(int n){ vector<int> ve; for(int i=1;i<=2*n;++i) ve.push_back(i); while(ve.size()){ vector<int> duli; vector<int> oth; for(int i=0;i<ve.size();++i){ if(!duli.size()||ask(duli,ve[i])) duli.push_back(ve[i]); else oth.push_back(ve[i]); } for(int i=0;i<oth.size();++i){ vector<int> rec=duli; do{ int p=findans(oth[i],rec); vector<int> ne; for(int j=0;j<rec.size();++j) if(rec[j]!=p) ne.push_back(rec[j]); rec=ne; if(ask(rec,oth[i])) break; }while(rec.size()); } ve=oth; } for(int i=1;i<=2*n;++i){ if(p[i].size()==1){ if(vis[i]||vis[p[i][0]]) continue; Answer(i,p[i][0]); vis[i]=vis[p[i][0]]=1; } else{ for(int j=0;j<=2;++j){ if(ask(i,p[i][j],p[i][(j+1)%3])==1){ lov[i]=p[i][(j+2)%3];whlov[lov[i]]=i; break; } } } } for(int i=1;i<=2*n;++i){ if(vis[i]) continue; for(int k=0;k<=2;++k){ if(p[i][k]==lov[i]||p[i][k]==whlov[i]) continue; if(vis[p[i][k]]) continue; vis[i]=vis[p[i][k]]=1; Answer(i,p[i][k]); break; } } }