专门为这题写个题解。
Description
给定一个仅包含 1 , 2 , 3 1,2,3 1,2,3的序列,每次这个序列的长度减 1 1 1,第 i i i位上的数变为原来序列中第 i i i位减去第 i + 1 i+1 i+1位的数的绝对值。
当这个序列的长度变成 1 1 1时,唯一那个数的值。
Solution
首先,我们可以发现几个性质:
①经历了第一次变换后,
3
3
3消失了,
0
0
0可能会出现。
这个很容易证:
1
,
2
,
3
1,2,3
1,2,3中任选两个可以相同的数相减的值的绝对值,一定不为
3
3
3。
②答案为
0
0
0或
1
1
1或
2
2
2。
证明方法与①同理。
③如果经历了一次变换后序列中存在
1
1
1,则答案不能为
2
2
2。
假设第
i
i
i个位置为
1
1
1,在第
i
i
i个位置左边的数为
0
0
0或
1
1
1或
2
2
2。经历了一次变换后,位置为
i
−
1
i-1
i−1的这个数只能是
0
0
0或
1
1
1,因为
∣
2
−
1
∣
=
∣
0
−
1
∣
=
1
,
∣
1
−
1
∣
=
0
|2-1|=|0-1|=1, |1-1|=0
∣2−1∣=∣0−1∣=1,∣1−1∣=0。扩展到整个序列的情况,
2
2
2会因为
1
1
1的存在慢慢消失。
于是,我们先对这个序列做一次变换,并开始大力分讨。
假设当前序列中只有 0 0 0和 1 1 1。可以发现,最终答案只能是 0 0 0或 1 1 1。
由于 a = 0 / 1 , b = 0 / 1 a=0/1, b=0/1 a=0/1,b=0/1时有 a + b = ∣ a − b ∣ a+b=|a-b| a+b=∣a−b∣,所以,我们每次的变换相当于"第 i i i位上的数变为原来序列中第 i i i位加上第 i + 1 i+1 i+1位的数"。
我们用字母写下这个序列:
a b c d e
经历许多次变换后,序列分别长这样:
a b c d e
a+b b+c c+d d+e
a+2b+c b+2c+d c+2d+e
a+3b+3c+d b+3c+3d+e
a+4b+6c+4d+e
观察一下第一列的数的各项系数,可以发现:这与杨辉三角形有关!
即,假设 01 01 01序列为 a 1 , a 2 , a 3 … … a n a_1,a_2,a_3……a_n a1,a2,a3……an,那么答案就是 ( ∑ i = 1 n C n − 1 i − 1 a i ) m o d 2 (\sum_{i=1}^n C_{n-1}^{i-1} a_i)\ mod\ 2 (i=1∑nCn−1i−1ai) mod 2
假设 k ! k! k!中质因数 2 2 2的个数为 n u m k num_k numk,那么 C x y C_x^y Cxy的质因数 2 2 2的个数就是 n u m x − n u m y − n u m x − y num_x-num_y-num_{x-y} numx−numy−numx−y。如果这个值为正,那么就是偶数;否则就是奇数。于是, 01 01 01序列的情况就处理完了。
注意到,这个序列中可能还含有 2 2 2。我们继续大力分讨。
假设这个序列只有 0 0 0和 2 2 2,显然最终答案只能是 0 0 0或 2 2 2,不能是 1 1 1。但是,我们仍然可以将这个序列转化为 01 01 01序列的情况。即,我们将这个 02 02 02序列中每个数都除以 2 2 2,变成 01 01 01序列;最终的答案为 01 01 01序列的答案的两倍。
最后一种情况: 这个序列存在 0 , 1 , 2 0,1,2 0,1,2。
可以发现,根据性质③中的"如果经历了一次变换,序列中存在 1 1 1,则答案不能为 2 2 2",得到答案只能为 0 0 0或 1 1 1。于是,我们可以将 2 2 2变成 0 0 0,此时转化为 01 01 01序列就直接用上述方法去算,最终答案显然不变。
于是,这道神仙题就做完了。时间复杂度 O ( n ) O(n) O(n)。
有必要总结一下。这题中,我们可以通过打表或思考来发现三个性质,然后将模型简化为 01 01 01序列,再扩展为其他情况,最终通过找规律与组合数、数论巧妙地解决了本题。
作为一道 A G C AGC AGC的 B B B题,这是比较难的,但是毫无疑问,本题考查得十分综合,是一道不可多得的好题。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxl=1000005;
int n,ans=0;
int a[maxl],b[maxl],c[maxl],with_2[maxl];
int C(int x,int y)
{
int now=with_2[x]-with_2[x-y]-with_2[y];
if (now) return 0;
else return 1;
}
signed main()
{
cin>>n;
for (int i=1;i<=n;i++)
{
char x;
cin>>x;
a[i]=x-'0';
}
if (n<=2) return cout<<abs(a[1]-a[2])<<endl,0;
for (int i=1;i<=n-1;i++) b[i]=abs(a[i]-a[i+1]);
int flag=1;
for (int i=1;i<=n-1;i++)
{
if (b[i]==1) flag=0;
}
if (flag==1)
{
for (int i=1;i<=n;i++) b[i]/=2;
}
for (int i=1;i<=n-2;i++) c[i]=abs(b[i]-b[i+1]);
n-=2;
for (int i=1;i<=n;i++)
{
with_2[i]=with_2[i-1];
int saver=i,cnt=0;
while (!(saver&1)) saver>>=1,cnt++;
with_2[i]+=cnt;
}
for (int i=1;i<=n;i++) ans=(ans+C(n-1,i-1)*c[i])%2;
if (flag==0) cout<<ans<<endl;
else if (flag==1)
{
if (ans==1) cout<<2<<endl;
else cout<<0<<endl;
}
return 0;
}