AtCoder 123 Triangle「思维题」

题目描述

这不是个链接
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\n",ans);
	return 0;
}
posted @ 2020-10-30 20:50  HH_Halo  阅读(151)  评论(0编辑  收藏  举报