1. 程式人生 > 其它 >【CF1129E】Legendary Tree

【CF1129E】Legendary Tree

題目

題目連結: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;
}