Codeforces 1081F Tricky Interactor [奇偶性]

题目链接
题意:让猜出一个长度为 n n n的01串,每次询问 [ l , r ] [l,r] [l,r],随机翻转 [ 1 , r ] 或 [ l , n ] [1,r]或[l,n] [1,r][l,n],返回翻转后序列中1的个数。翻转就是把每一位上的数取反。在10000次询问内结束。 n ≤ 300 n\le 300 n300

题解

随机翻转让人很难过,不妨来分析一下如何才能准确判断翻转的是左边还是右边。
不难发现,翻转长度为奇数的区间会导致1的变化量为奇数;否则变化量为偶数。也就是说,我们如果让 [ 1 , r ] [1,r] [1,r] [ l , n ] [l,n] [l,n]长度的奇偶性不同,就可以判断出翻转的是哪边的了。
其次,由于是随机翻转,很难达到我们想要的效果。我们想要的无非就是翻转固定的区间,这可以通过多次翻转实现。由于是随机的,我们可以证明期望恰好翻转左右中的一个区间,期望翻转次数是3次。
于是思路就比较明了了,分 n n n的奇偶性讨论:

n n n为偶数,则任意 i i i n − i + 1 n-i+1 ni+1的奇偶性不同,于是我们可以通过翻转前缀来得到原串的前缀和。
设前缀中原来有 x x x个1,长度为 i i i,翻转前和翻转后1的总数分别为 t , t ′ t,t' t,t,则 t ′ = t − x + i − x t'=t-x+i-x t=tx+ix,易解出 x x x

n n n为奇数,则任意 i i i n − i n-i ni的奇偶性不同,我们每次操作 [ i , i + 1 ] [i,i+1] [i,i+1],也可以得到翻转前缀的效果。但是第一个数和第二个数无法求出。于是我们考虑翻转 [ 2 , n ] [2,n] [2,n]这个后缀,于是可以求出 [ 2 , n ] [2,n] [2,n]中有多少个1,问题就完美解决了。

注意每次翻完后要翻回来,以及特判 n = 1 n=1 n=1的情况。期望操作次数为 6 n 6n 6n

#include <bits/stdc++.h>
using namespace std;

int n, cnt;
int ask(int l, int r) {
	printf("? %d %d\n", l, r);
	fflush(stdout);
	int t; scanf("%d", &t);
	return t;
}
int turn(int l, int r, int t, int er = 1, int el = 0) {//turn [1,r]
	int gl = 0, gr = 0;
	while (true) {
		int t1 = ask(l, r);
		if (((t - t1) & 1) == (r & 1)) gr ^= 1;
		else gl ^= 1;
		if (gr == er && gl == el) return t1;
		t = t1;
	}
}
char str[305];
int main() {
	scanf("%d%d", &n, &cnt);
	if (n == 1) printf("! %c\n", '0' + cnt);
	else if (n & 1) {
		int lst = 0;
		for (int i = 1; i < n; i++) {
			int t = turn(i, i + 1, cnt);
			int a = (cnt + i - t + 1) >> 1;
			if (i > 1) str[i + 1] = a - lst + '0';
			lst = a;
			turn(i, i + 1, t);
		}
		int t = turn(2, n, cnt, 0, 1);
		int s = (cnt - t + n - 1) >> 1;
		for (int i = 3; i <= n; i++) s -= str[i] - '0';
		str[2] = s + '0';
		for (int i = 2; i <= n; i++) cnt -= str[i] - '0';
		str[1] = cnt + '0';
		printf("! %s\n", str + 1);
	} else {
		int lst = 0;
		for (int i = 1; i <= n; i++) {
			int t = turn(i, i, cnt);
			int a = (cnt + i - t) >> 1;
			str[i] = '0' + a - lst;
			lst = a;
			turn(i, i, t);
		}
		printf("! %s\n", str + 1);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值