题面
题意
交互题,要求你通过询问确定一个有n个节点的树的边,每次询问输出两个集合S,T和点v,将会告诉你从S集合的某个点到T集合的某个点并经过点v的路径数。
做法
首先钦定点1为根节点,然后发现,询问({
x
∈
N
∗
x \isin N^*
x∈N∗ |
2
<
=
x
<
=
n
,
x
̸
=
i
2<=x<=n,x\not =i
2<=x<=n,x̸=i},{1},
i
i
i)可以得到i点的子树大小,然后根据字树大小从小到大排序之后就能发现,一个点的儿子全部在他左边。
可以用一个set维护还没确定父亲但已经确定儿子的点(一开始把所有叶子节点放进去),然后就要对一个点求在set中哪些是他的儿子。对此可以将set分成两半,用一次询问判断左边这半是否有点是他的儿子,如果有则左边递归下去,右边同样处理,可以发现这样确定某个点的父亲只要至多log次询问。
代码
#include<bits/stdc++.h>
#define P pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define N 510
using namespace std;
int n,fa[N];
P num[N];
set<int>node;
inline void out()
{
int i,j;
puts("ANSWER");
for(i=2;i<=n;i++) printf("%d %d\n",fa[i],i);
}
void dfs(int rt,set<int>::iterator now,int cnt)
{
if(cnt==1)
{
fa[*now]=rt;
node.erase(now);
return;
}
int i,t,mid=(cnt>>1);
set<int>::iterator tmp=now;
printf("%d\n",mid);
for(i=1;i<=mid;i++,now++)
{
printf("%d ",*now);
}
printf("\n1\n1\n%d\n",rt);fflush(stdout);
scanf("%d",&t);
if(t) dfs(rt,tmp,mid);
printf("%d\n",cnt-mid);
tmp=now;
for(i=mid+1;i<=cnt;i++,now++)
{
printf("%d ",*now);
}
printf("\n1\n1\n%d\n",rt);fflush(stdout);
scanf("%d",&t);
if(t) dfs(rt,tmp,cnt-mid);
}
int main()
{
int i,j;
cin>>n;
if(n==2)
{
fa[2]=1;
out();
return 0;
}
for(i=2;i<=n;i++)
{
num[i-1].se=i;
printf("%d\n",n-2);
for(j=2;j<=n;j++)
{
if(i==j) continue;
printf("%d ",j);
}
printf("\n1\n1\n%d\n",i);
fflush(stdout);
scanf("%d",&num[i-1].fi);
}
sort(num+1,num+n);
for(i=1;i<n;i++)
{
if(!num[i].fi)
{
node.insert(num[i].se);
continue;
}
dfs(num[i].se,node.begin(),node.size());
node.insert(num[i].se);
}
for(set<int>::iterator it=node.begin();it!=node.end();it++) fa[*it]=1;
out();
}