codeforces1129E Legendary Tree

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzyyylx/article/details/87925498

题面

题意

交互题,要求你通过询问确定一个有n个节点的树的边,每次询问输出两个集合S,T和点v,将会告诉你从S集合的某个点到T集合的某个点并经过点v的路径数。

做法

首先钦定点1为根节点,然后发现,询问({ x N x \isin N^* | 2 < = x < = n , x ̸ = i 2<=x<=n,x\not =i },{1}, 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();
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/87925498