专栏文章

CF2037E 题解

CF2037E题解参与者 4已保存评论 3

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
3 条
当前快照
1 份
快照标识符
@mir2c1ms
此快照首次捕获于
2025/12/04 14:36
3 个月前
此快照最后确认于
2025/12/04 14:36
3 个月前
查看原文
不错的题。
首先,不能这么做:
  • 尝试把原来的序列分成若干个小序列。
  • 尝试对小序列进行大分讨。
这样做的不足之处:
  • 分类讨论情况太多了,询问次数会超。
  • 断成若干个小序列,等于忽略了不同序列之间的条件,导致答案错误。
我们发现最多询问 nn 次,那么整体就是线性的,我们不妨考虑如何 Θ(n)\Theta(n) 地进行推导。接下来设 f(l,r)f(l,r) 表示对 [l,r][l,r] 的询问。
可以发现如果 f(1,i)=0f(1,i)=0 但是 f(1,i+1)=k,k>0f(1,i + 1)=k,k>0 的话,我们可以得到如下结论:
  • 由于 f(1,i+1)0f(1,i + 1)\ne 0,所以 [1,i][1,i] 中一定存在 00
  • 由于 f(1,i)=0f(1,i)=0,那么这一段一定没有任何一个 0011 的前面。
综上,我们可以得出:此时 [1,i][1,i] 一定是由一段前缀的 11 和一段后缀的 00 组成,可以没有 11
那么后缀的 00 的长度是多少呢?可以发现,如果 i+1i+1 仍然是 00,那么 f(1,i+1)=0f(1,i+1)=0,所以位置 i+1i+1 一定是 11;由于 f(1,i+1)=kf(1,i+1)=k,那么意味着(注意到只有这个 11 才有机会和前面的 00 匹配)这个 11kk00 匹配了,所以后缀的 00 的长度是 kk,然后顺便直接把前面的 11 填进去就好了。
之后该怎么办呢?注意到另一件事,就是左端点不动,右端点单增时,答案不可能变小,那么我们找到第一个满足上述条件的位置 ii,之后对于 ii 后面的位置 jj
  • 如果 f(1,j)=f(1,j1)f(1,j)=f(1,j-1),那么说明 jj 这个位置一定无法和前面配对,由于我们的 ii 符合刚才说的条件,那么此时 jj 前面一定有 00 存在,如果它没有配成,说明这个位置一定是 00(要是是 11 一定可以和前面的 00 做出贡献)。
  • 反之则为 11
那么我们直接再扫一遍就可以了。
什么时候没有唯一解呢?就是没有找到 ii 的时候,很好理解。
代码:
CPP
#include <bits/stdc++.h>
#define f(i ,m ,n ,x) for (int i = (m) ; i <= (n) ; i += (x))
using std :: cin ; using std :: cout ; using std :: endl ;
int t ,n ,ch[10007] ; 
int main () {
	cin >> t ;
	while (t --) {
		cin >> n ;
		int ans = 0 ; bool flag (0) ;
		cout << "? " << 1 << ' ' << 2 << endl ;
		cin >> ans ;
		if (ans == 1) { ch[1] = 0 , ch[2] = 1 ,flag = true ;}
		f (i ,3 ,n ,1) {
			cout << "? " << 1 << ' ' << i << endl ;
			int res ; cin >> res ;
			if (flag == false) {
				if (res ^ 0) {
					f (j ,1 ,i - 1 - res ,1) ch[j] = 1 ;
					f (j ,i - res ,i - 1 ,1) ch[j] = 0 ;
					ch[i] = 1 , flag = true ;
				} 
				ans = res ;
			} else {
				if (res == ans) ch[i] = 0 ;
				else ch[i] = 1 ;
				ans = res ;
			}
		}
		if (ans == 0) cout << "! IMPOSSIBLE" << endl ;
		else {
			cout << "! " ;
			f (i ,1 ,n ,1) cout << ch[i] ; cout << endl ;
		}
	}
	return 0 ;
}

评论

3 条评论,欢迎与作者交流。

正在加载评论...