Codeforces 1129E - Legendary Tree(思維題)
阿新 • • 發佈:2021-07-25
人類智慧思維題,直接爬了/wq
中編號最小的 \(mid\) 個點組成的集合,\(T=\{1\}\),\(u=x\),如果互動庫返回的值 \(>0\) 就向左二分,否則向右二分,二分出第一個後再找第二個、第三個……以此類推。刪完這些點之後加入將 \(x\) 加入集合,如此進行下去即可。最後掃描到 \(1\) 時就將 \(st\) 中的點的父親全部設為 \(1\) 並清空 \(st\)
考慮以 \(1\) 為根,記 \(siz_i\) 為 \(i\) 子樹的大小,那麼可以通過詢問 \(S=\{2,3,\cdots,n\},T=\{1\}\) 以及每個 \(u\) 得出 \(siz_u\) 的值。
考慮將所有點按 \(siz\) 從小到大排序並維護一個集合 \(st\) 表示目前還沒有找到父親的點的集合,那麼我們列舉到一個點 \(x\) 時就在 \(st\) 中二分找到所有父親為 \(x\) 的點並將它們從 \(st\) 中刪除,具體步驟是,我們先二分找出 \(st\) 中編號最小的點,即二分出一個 \(mid\) 後就詢問 \(S=st\)
時間複雜度 \(n\log^2n\),詢問次數 \(n\log n\),實測在 \(7000\) 左右,可以通過此題的限制。
看到沒?什麼超綱的演算法都沒有。所以啊,菜是原罪/kk——Codeforces Round #691 (Div.2) 題解
const int MAXN=500; int n,siz[MAXN+5],ord[MAXN+5];set<int> st; bool cmp(int x,int y){return siz[x]<siz[y];} bool check(int x,int l,int r){ set<int>::iterator it=st.begin(); for(int i=1;i<l;i++) ++it;printf("%d\n",r-l+1); for(int i=1;i<=r-l+1;i++) printf("%d%c",*it++," \n"[i==r-l+1]); printf("1\n1\n%d\n",x);fflush(stdout); int t;scanf("%d",&t);return t>0; } int main(){ scanf("%d",&n);siz[1]=n; for(int i=2;i<=n;i++){ printf("%d\n",n-1); for(int j=2;j<=n;j++) printf("%d%c",j," \n"[j==n]); printf("1\n1\n%d\n",i);fflush(stdout); scanf("%d",&siz[i]); } for(int i=1;i<=n;i++) ord[i]=i;sort(ord+1,ord+n+1,cmp); vector<pii> ans; for(int i=1;i<n;i++){ if(!st.empty()){ int cur=0;vector<int> son; while(cur<st.size()){ int l=cur+1,r=st.size(),p=st.size()+1; while(l<=r){ int mid=l+r>>1; if(check(ord[i],cur+1,mid)) p=mid,r=mid-1; else l=mid+1; } if(p!=st.size()+1){ set<int>::iterator it=st.begin(); for(int j=1;j<p;j++) ++it; son.pb(*it); } cur=p; } for(int x:son) st.erase(st.find(x)),ans.pb(mp(x,ord[i])); } st.insert(ord[i]); } printf("ANSWER\n"); for(int x:st) ans.pb(mp(x,1)); for(pii p:ans) printf("%d %d\n",p.fi,p.se); fflush(stdout); return 0; }