题目描述
这不是个链接
luogu
思路分析
- 不难发现,这道题在从 (1) 到 (n) 一层一层的不断计算之中,将每层的数放在一起,就形成了一个形状和杨辉三角一样的东西,当然,也有和杨辉三角类似的性质下面会用到
- 如果你自己手膜了数据的话,会发现 (3) 在第二层就已经一定不存在了,因为没有两个数的差值等于 (3),所以最后答案只可能是 (0),(1),(2)。
- 继续计算,会发现 (1) 无论是遇到 (0) 还是 (2),最后仍然还是 (1),这意味着如果不存在一层的所有数都是 (1),那么最终答案也一定是 (1),所以得出一条重要结论:如果序列中存在 (1),那么最终答案一定为 (0) 或 (1),这时候 (0) 和 (2) 其实是等价的,所以转化为 (mod) (2) 意义下。延伸得到另一条结论:如果不存在 (1),那么最终答案一定为 (0) 或 (2)
- 上面两种情况没有什么本质区别,第二种也完全可以全部除以 (2),转化成 (0/1) 序列。而对于 (0/1) 序列来说(即在 (mod) (2)意义下),加减操作和异或操作是等价的,我们只需要进行异或计算就好了。
- 这时候只需求出最初每个数(指相邻两数之间的差值)的异或次数,而上面说到,这其实是一个杨辉三角,而杨辉三角中的每个数都可以用组合数求出,这个组合数的值的本质含义其实就是三角顶层的那个 (1) 到达这个数的路径数,所以将最初的每个数放到杨辉三角上,就可以用组合数求出异或次数,第 (i) 个数的次数即为 (C_{n-1}^{i-1})。
- 最后用 (Lucas) 定理快速求出组合数值,(Lucas(n/2,m/2)*C(n\%2,m\%2)) 其实最后结果就是(n&m==m),不明白可以联系二进制考虑,如果有一位 (2) 进制上 (n) 为 (0),(m) 为 (1),那么计算结果就是 (C_0^1) ,也就是 (0)
(Code)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1000010
#define R register
using namespace std;
inline int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,ans;
char s[N],a[N];
inline int C(int x,int y){return (x&y)==y;}
int main(){
n = read()-1;
scanf("%s",s+1);
for(R int i = 1;i <= n;i++)a[i] = abs(s[i]-s[i+1]);
bool flag = 0;//判断有无 1
for(R int i = 1;i <= n;i++)flag |= a[i]==1;
if(!flag)for(R int i = 1;i <= n;i++)a[i]>>=1;
for(R int i = 1;i <= n;i++){
ans ^= C(n-1,i-1)?(a[i]&1):0;//(a[i]&1)是因为在 mod2 意义下,防止出现 ^2 的情况
}
if(!flag)ans<<=1;
printf("%d
",ans);
return 0;
}