• 【CF1370F2】The Hidden Pair (Hard Version)


    题目

    题目链接:https://codeforces.com/problemset/problem/1370/F2
    这是一道交互题。困难版与简单版唯一的区别是交互次数限制。

    本题有多组数据。

    已知一棵 (n) 个顶点的树(边集已知),其中有两个不同的顶点被暗中做了标记。你现在需要通过若干次询问,猜出两个被标记顶点的编号。

    一次询问的格式为 ? c x_1 x_2 ... x_c,即代表你向交互库请求关于 (x_1,x_2,cdots,x_c)(c) 个点的信息。

    对于一次询问,交互库的返回格式为 x d,表示在询问的集合中,到两个被标记点的距离之和最小的点是 (x),这个最小值为 (d)。如果有多个最小值点,(x) 的值可能是其中任意一个。

    如果已经知晓答案,请用 ! x y 的格式来输出你的答案,任意顺序均可。在这之后,你会收到一个字符串 Correct 或者 Incorrect,代表你的猜测是否正确。如果收到了 Incorrect,请立即终止程序,否则请继续处理下一组数据。

    对于每组数据,请你在不超过 (11) 次询问之内给出答案。(Easy Version 是 (14) 次)

    (1le tle 10, 2le nle 1000)

    思路

    首先询问一遍所的点,可以得到两个标记点的距离 (dis),以及位于它们路径上的一个点。
    然后以得到的这个点开始 dfs,用 vector 记录每一个点的深度。
    然后二分深度,每次询问这个深度上的所有点。如果得到的距离是 (dis),说明较深的那一个点一定深度不小于当前二分的深度。
    可以在 (O(log n)leq 10) 次询问找到其中一个标记点。以这个标记点为根 dfs,然后询问所有深度为 (dis) 的点,得到的就是另一个标记点了。
    操作次数为 (1+10+1=12),可以过 Easy Version。
    我们发现,因为第一次二分的是较深的点,它的深度一定不小于 (lceilfrac{dis}{2} ceil),这样我们二分的区间就变成了 ([lceilfrac{dis}{2} ceil,min(dis,maxdep)])。显然这个的上界是 (frac{n}{2}leq 500),那么此时的操作次数为 (1+log frac{n}{2}+1geq 1+9+1=11)。可以通过 Hard Version。
    时间复杂度 (O(Qnlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1010;
    int n,Q,rt,dis,res,maxd,tot,head[N];
    char ch[20];
    vector<int> node[N];
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    void prework()
    {
    	for (int i=0;i<=maxd;i++) node[i].clear();
    	memset(head,-1,sizeof(head));
    	tot=maxd=0;
    }
    
    void dfs(int x,int fa,int dep)
    {
    	maxd=max(maxd,dep);
    	node[dep].push_back(x);
    	for (int i=head[x];~i;i=e[i].next)
    		if (e[i].to!=fa) dfs(e[i].to,x,dep+1);
    }
    
    int main()
    {
    	scanf("%d",&Q);
    	while (Q--)
    	{
    		prework();
    		scanf("%d",&n);
    		for (int i=1,x,y;i<n;i++)
    		{
    			scanf("%d%d",&x,&y);
    			add(x,y); add(y,x);
    		}
    		
    		// -----------------------------------------------------------
    		printf("? %d",n);
    		for (int i=1;i<=n;i++)
    			printf(" %d",i);
    		printf("
    ");
    		fflush(stdout);
    		// -----------------------------------------------------------
    		
    		scanf("%d%d",&rt,&dis);
    		dfs(rt,0,0);
    		int l=dis/2,r=min(maxd,dis),mid,x,y;
    		while (l<=r)
    		{
    			mid=(l+r)>>1;
    			
    			// -----------------------------------------------------------
    			printf("? %d",node[mid].size());
    			for (int i=0;i<node[mid].size();i++)
    				printf(" %d",node[mid][i]);
    			printf("
    ");
    			fflush(stdout);
    			// -----------------------------------------------------------
    			
    			scanf("%d%d",&x,&y);
    			if (y==dis) l=mid+1,res=x;
    				else r=mid-1;
    		}
    		for (int i=0;i<=maxd;i++) node[i].clear();
    		dfs(res,0,0);
    		
    		// -----------------------------------------------------------
    		printf("? %d",node[dis].size());
    		for (int i=0;i<node[dis].size();i++)
    			printf(" %d",node[dis][i]);
    		printf("
    ");
    		fflush(stdout);
    		// -----------------------------------------------------------
    		
    		scanf("%d%d",&x,&y);
    		printf("! %d %d
    ",x,res);
    		fflush(stdout);
    		scanf("%s",ch);
    	}
    	return 0;
    }
    
  • 相关阅读:
    _proto_和prototype的区别
    ajax
    图片预加载
    loading动画
    WinSCP
    检测竖屏 横屏
    webstrom hbuilder快捷键
    vue 引入sass
    npm install -save 和 -save-dev 区别
    打乱数组顺序
  • 原文地址:https://www.cnblogs.com/stoorz/p/14211018.html
Copyright © 2020-2023  润新知