【CF1129E】Legendary Tree
阿新 • • 發佈:2021-06-24
題目
題目連結:https://codeforces.com/problemset/problem/1129/E
這是一道互動題。
有一個 \(n\) 個節點的樹,你需要通過不超過 \(11111\) 次詢問得知樹的形態。
詢問方式為給出兩個非空無交點集 \(S,T\) 和一個點 \(u\),可以得到滿足 \(s \in S , t \in T\) 且路徑 \((s,t)\) 經過 \(u\) 點的二元組 \((s,t)\) 的總數。
\(n\leq 500\)。
思路
樹上互動題都是神仙吧。
設 \(1\) 為根,首先可以通過 \(n-1\) 次詢問得到每一個點的子樹大小(取 \(S=\{1\},T=\{2,3,\cdots n\},u=i\)
將點按照子樹大小排序,那麼對於序列中的任意一個點,他的兒子都在他前面。
假設按照子樹大小列舉到點 \(i\),維護一個集合 \(s\) 表示 \(i\) 子樹比他小的點,且未選擇父親的點集。
然後可以詢問一次得到點集中有多少個點的父親是 \(i\),接下來分別找到每一個兒子,可以採用二分。
這樣的話只會二分 \(n-1\) 次,時間複雜度 \(O(n^2\log n)\),次數上界為 \(2n+n\log n\leq 5500\)。
程式碼
#include <bits/stdc++.h> using namespace std; const int N=510; int n,siz[N],id[N],fa[N]; vector<int> s; bool cmp(int x,int y) { return siz[x]<siz[y]; } int print(int x,int l,int r) { cout<<"1\n1\n"<<r-l+1<<"\n"; for (int i=l;i<=r;i++) cout<<s[i]<<" "; cout<<"\n"<<x<<"\n"; fflush(stdout); scanf("%d",&x); return x; } int main() { scanf("%d",&n); for (int i=2;i<=n;i++) s.push_back(i); siz[1]=n; id[1]=1; for (int i=2;i<=n;i++) siz[i]=print(i,0,n-2),id[i]=i; s.clear(); sort(id+1,id+1+n,cmp); for (int i=1;i<=n;i++) { if (s.size()) { int cnt=print(id[i],0,s.size()-1),last=-1; while (cnt--) { int l=last+1,r=s.size()-1,mid; while (l<=r) { mid=(l+r)>>1; if (print(id[i],l,mid)) r=mid-1; else l=mid+1; } fa[s[r+1]]=id[i]; for (int i=r+2;i<s.size();i++) s[i-1]=s[i]; s.pop_back(); } } s.push_back(id[i]); } cout<<"ANSWER\n"; for (int i=2;i<=n;i++) cout<<i<<" "<<fa[i]<<"\n"; fflush(stdout); return 0; }