• [ARC070D] HonestOrUnkind


    一、题目

    点此看题

    二、解法

    我竟然做得起交互题了,而且还是 ARC F 级别的交互题!

    首先拿到这题真是一点思路都没有,那么我不妨把所有 \((x,y)\) 都询问出来,如果结果是 ? 就连一条 \(x\rightarrow y\) 的有向边。那么考虑诚实的人的导出子图一定是一个完全图,并且没有连向外界的边。

    那么我们称这个导出子图中的点是互证清白的,我们还可以利用的信息是两个数量 \(a,b\),当 \(a\leq b\) 时,不友好的人为了不让我们猜中,可以模仿一个 \(a\) 阶互证清白子图,那么我们一定无法区分;当 \(a>b\) 时,可以通过数量确定唯一的互证清白子图。

    这说明了一个关键结论:有解的充要条件是 \(a>b\)

    但是真正构造答案肯定不能去找完全图,考虑把询问拆分成 \(n+n\) 的形式,即用 \(n\) 次询问去寻找诚实的人,用 \(n\) 次询问向这个诚实的人问出所有信息。

    主要矛盾在第一步,但是由于只有恰好 \(n\) 次操作我们询问的方式是很有限的。这里不妨直接线性扫了,然后及时排除掉不友好的人。我们维护一个可能诚实人的栈,每次对于一个新的人,用栈顶询问它的信息。

    如果结果是 N,说明这两者之中必有一个不友好的人,把这两个人都排除掉,由于 \(a>b\) 最后一定会剩下诚实的人。然后考虑最后得到的栈中,不友好的人一定是栈的一个前缀,否则一定会被排除掉,那么取栈顶就是诚实的人。

    时间复杂度 \(O(n)\),询问次数 \(\leq 2n\)

    三、总结

    当交互题没有任何思路时,不妨假设自己知道所有信息,然后看能用出什么效果。

    用到的小技巧:建立图论模型思考问题、对询问次数进行合理的拆分、根据次数反推做法、确定关键点从而确定全局。

    #include <cstdio>
    const int M = 2005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a,b,z[M],ans[M];char s[M];
    void ask(int x,int y)
    {
    	printf("? %d %d\n",x,y);
    	fflush(stdout);scanf("%s",s);
    }
    signed main()
    {
    	a=read();b=read();n=a+b;
    	if(a<=b) {puts("Impossible");return 0;}
    	for(int i=0;i<n;i++)
    	{
    		if(!m) {z[++m]=i;continue;}
    		ask(z[m],i);
    		if(s[0]=='N') m--;
    		else z[++m]=i;
    	}
    	int x=z[m];
    	for(int i=0;i<n;i++)
    		ask(x,i),ans[i]=s[0]=='Y';
    	printf("! ");
    	for(int i=0;i<n;i++) printf("%d",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    9-单表查询
    02-数据库概述
    01-MySql的前戏
    mysql+centos7+主从复制
    Mac下安装ipython与jupyter
    python开发之virtualenv与virtualenvwrapper讲解
    python操作redis
    权限管理具体代码实现
    docker入门
    多用判断&&
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16354777.html
Copyright © 2020-2023  润新知