【题目】
原题地址
有一个长度为
n
n
n的
01
01
01串,初始告诉你串中
1
1
1的个数,你可以进行不超过
1
0
4
10^4
104次操作,每次操作形如
(
l
,
r
)
(l,r)
(l,r),将会随机使区间
[
1
,
r
]
[1,r]
[1,r]或
[
l
,
n
]
[l,n]
[l,n]其中一段全部取反,并返回现在串中
1
1
1的个数,求原串。
n
≤
300
n\leq 300
n≤300
【解题思路】
首先我们知道一个结论:翻转一个区间,则这个全局中
[
1
个
数
的
变
化
量
]
≡
[
区
间
长
度
]
(
mod
2
)
[1个数的变化量]\equiv [区间长度](\text{mod}\ 2)
[1个数的变化量]≡[区间长度](mod 2)
- 设一次翻转区间 [ l , r ] [l,r] [l,r],全局 1 1 1的个数从 x x x变成 y y y,这个区间内原来有 t t t个 1 1 1则 y − x = ( r − l + 1 ) − 2 t y-x=(r-l+1)-2t y−x=(r−l+1)−2t。
所以我们如果让 [ 1 , l ] [1,l] [1,l]和 [ r , n ] [r,n] [r,n]的长度不同,我们就可以知道翻转的是哪一个区间,我们也就知道了。
现在还要解决的问题是怎么样使得这个串翻转我们期望翻转的一段。实际上我们如果想单独翻转 [ 1 , l ] [1,l] [1,l]这一段,我们只需要一直对 [ l , r ] [l,r] [l,r]操作即可,期望操作次数应该是 3 3 3次,具体证明的话也不难:
- 我们令 f 0 / 1 , 0 / 1 f_{0/1,0/1} f0/1,0/1表示每一段翻不翻转时期望多少步能翻转到 f 1 , 0 f_{1,0} f1,0,我们可以列出等式: f 0 , 0 = f 0 , 1 + f 1 , 0 2 + 1 , f 0 , 1 = f 0 , 0 + f 1 , 1 2 + 1 , f 1 , 1 = f 1 , 0 + f 0 , 1 2 + 1 f_{0,0}=\frac {f_{0,1}+f_{1,0}} 2+1,f_{0,1}=\frac {f_{0,0}+f_{1,1}} 2+1,f_{1,1}=\frac {f_{1,0}+f_{0,1}} 2+1 f0,0=2f0,1+f1,0+1,f0,1=2f0,0+f1,1+1,f1,1=2f1,0+f0,1+1. 那么 f 0 , 0 = 3 , f 0 , 1 = 4 , f 1 , 1 = 3 f_{0,0}=3,f_{0,1}=4,f_{1,1}=3 f0,0=3,f0,1=4,f1,1=3
综上,我们可以得到下面这个做法:我们设 s i s_i si表示第 i i i位的数字。
- 当 n n n是奇数,我们每次应当询问一个长度为偶数的区间,于是我们可以查询 ( 2 , n ) , ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 4 ) … ( n − 2 , n − 1 ) (2,n),(1,2),(2,3),(3,4)\dots (n-2,n-1) (2,n),(1,2),(2,3),(3,4)…(n−2,n−1),目的是为了翻转 [ 2 , n ] , [ 1 , 2 ] , [ 1 , 3 ] … [ 1 , n − 1 ] [2,n],[1,2],[1,3]\dots[1,n-1] [2,n],[1,2],[1,3]…[1,n−1],这样我们可以知道 s 1 + s 2 , s 1 + s 2 + s 3 , … s_1+s_2,s_1+s_2+s_3,\dots s1+s2,s1+s2+s3,…以及 s 2 + s 3 + ⋯ + s n s_2+s3+\dots+s_n s2+s3+⋯+sn于是 s s s就唯一确定了。
- 当 n n n是偶数,我们每次应当询问一个长度为奇数的区间,于是我们可以查询 ( 1 , 1 ) , ( 2 , 2 ) , ( 3 , 3 ) … ( n − 1 , n − 1 ) (1,1),(2,2),(3,3)\dots (n-1,n-1) (1,1),(2,2),(3,3)…(n−1,n−1),目的是为了翻转 [ 1 , 1 ] , [ 1 , 2 ] , [ 1 , 3 ] … [ 1 , n − 1 ] [1,1],[1,2],[1,3]\dots[1,n-1] [1,1],[1,2],[1,3]…[1,n−1],这我们可以知道 s 1 , s 1 + s 2 , s 1 + s 2 + s 3 , … s_1,s_1+s_2,s_1+s_2+s_3,\dots s1,s1+s2,s1+s2+s3,…同样可以得到答案。
【参考代码】
#include<bits/stdc++.h>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N=1e4+10;
int n,cnt,a[N],ans[N];
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=0;c=getchar();}
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return f?ret:-ret;
}
bool flip(int l,int r)
{
printf("? %d %d\n",l,r);fflush(stdout);
a[++cnt]=read();return (a[cnt]-a[cnt-1])&1;
}
int main()
{
n=read();ans[n]=a[0]=read();
for(int i=1;i<n;++i)
{
int x=a[cnt],t[2]={0,0};
if(i==1 && n&1)
{
while(!t[0] || t[1]) t[flip(2,n)]^=1;
int y=a[cnt];
ans[i]=ans[n]-(n-1-y+x)/2;
while(t[0] || t[1]) t[flip(2,n)]^=1;
}
else
{
while(!t[0] || t[1]) t[(flip(i-(n&1),i)^i)&1]^=1;
int y=a[cnt];
ans[i]=(i-y+x)/2;
while(t[0] || t[1]) t[(flip(i-(n&1),i)^i)&1]^=1;
}
}
printf("! ");
for(int i=1;i<=n;++i) printf("%d",ans[i]-ans[i-1]);
puts("");fflush(stdout);
return 0;
}
【总结】
一道十分有趣的交互题。
集训队出题就是不一样。