【Atcoder】 [ARC156D] Xor Sum 5

文章介绍了一种基于异或性质和Lucas定理的动态规划方法,用于解决编程竞赛中的特定问题。它涉及到将数字异或操作与二进制位关联,并计算满足特定条件的数的异或和。通过建立状态转移方程,可以计算出每种情况的方案数,并最终得到答案。
摘要由CSDN通过智能技术生成

题目链接

Atcoder方向
Luogu方向

题目解法

考虑异或的性质,一个数被异或偶数次相当于 0 0 0,一个数被异或奇数次相当于只异或一次
考虑每个数若出现了 b 1 , b 2 , . . . , b n b_1,b_2,...,b_n b1,b2,...,bn 次,其中 b 1 + b 2 + . . . + b n = k b_1+b_2+...+b_n=k b1+b2+...+bn=k
那么这种情况出现的次数为 ( n b 1 ) ∗ ( n − b 1 b 2 ) ∗ . . . ∗ ( n − b 1 − b 2 − . . . − b n − 1 b n ) (^{b_1}_{n})*(^{b_2}_{n-b_1})*...*(^{b_n}_{n-b_1-b_2-...-b_{n-1}}) (nb1)(nb1b2)...(nb1b2...bn1bn)
如果次数需要为偶数,那么根据 l u c a s lucas lucas 定理, a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an 都必须是 k k k 二进制表示的子集

这把问题转化为了对于每个 k k k 的二进制位,都可以填上 a 1 , a 2 , . . , a n a_1,a_2,..,a_n a1,a2,..,an,求每种方案的数和的异或和
考虑数位 d p dp dp,令 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示到第 i i i 位时,前面的进位+当前加上的 a i a_i ai j j j 的方案数

  1. k k k 的第 i i i 位为 0, d p [ i ] [ j / 2 ]    − >    d p [ i − 1 ] [ j ] dp[i][j/2] \;->\;dp[i-1][j] dp[i][j/2]>dp[i1][j]
  2. k k k 的第 i i i 位为 1, d p [ i − 1 ] [ j ]    − >    d p [ i ] [ j / 2 + a i ] dp[i-1][j]\;->\;dp[i][j/2+a_i] dp[i1][j]>dp[i][j/2+ai]

最后统计答案的时候需要后面总的方案数与当前方案数的乘积为奇数,且 j j j 为奇数,答案才能异或上 2 i 2^i 2i

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N(1100);
int n,k,ans,a[N];
bool dp[55][N<<1]; 
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void calc(int p){
	int ways=1;
	for(int i=p+1;i<=51;i++) if(k>>(i-1)&1) ways=ways*n%2;
	if(!ways) return; 
	for(int i=1;i<N<<1;i+=2) if(dp[p][i]) ans^=1ll<<(p-1);
}
signed main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++) a[i]=read();
	dp[0][0]=1;
	for(int i=1;i<=51;i++){
		if(k>>(i-1)&1) for(int j=0;j<N<<1;j++) for(int l=1;l<=n;l++) dp[i][j/2+a[l]]^=dp[i-1][j]; 
		else for(int j=0;j<N<<1;j++) dp[i][j/2]^=dp[i-1][j];
		calc(i);
//		cout<<ans<<'\n';
	}
	printf("%lld",ans);
	return 0;
}
//dp[i][j]:到第i位当前位的和为j的方案数 %2 的值 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值