CF gym102586 E. Count Modulo 2【模2下定和选数的方案数】

题目描述:

给出 K K K 个互不相同的数 A i A_i Ai,求一个长为 N N N 的序列 { x j } , x j ∈ [ 1 , K ] \{x_j\},x_j\in[1,K] {xj},xj[1,K],满足 ∑ j = 1 N A x j = S \sum_{j=1}^NA_{x_j}=S j=1NAxj=S 的方案数模2的值。

K ≤ 200 , N , S ≤ 1 0 18 , 0 ≤ A i ≤ 1 0 5 K\le200,N,S\le10^{18},0\le A_i\le10^5 K200,N,S1018,0Ai105

题目分析:

设每个 A i A_i Ai 出现了 p i p_i pi 次,那么 ∑ p i = N , ∑ A i p i = S \sum p_i=N,\sum A_ip_i=S pi=N,Aipi=S,考虑对应到排列上,那么序列 { p } \{p\} {p} 对方案数的贡献次数是 N ! ∏ p i ! \frac {N!}{\prod p_i!} pi!N!

那么一个序列 { p } \{p\} {p} 的贡献模2余1,则要求上面的式子不能被2整除,我们知道式子中含2的因子数 = ∑ i = 0 ∞ ⌊ N 2 i ⌋ − ∑ j = 1 K ⌊ p j 2 i ⌋ =\sum_{i=0}^\infty\left\lfloor\frac N{2^i}\right\rfloor-\sum_{j=1}^K\left\lfloor \frac {p_j}{2^i}\right\rfloor =i=02iNj=1K2ipj

∑ p i = N \sum p_i=N pi=N,与库默尔定理类似,这意味着在二进制下做加法不能进位。即 p i p_i pi N N N 二进制下的子集
那么考虑 N N N 每一位 1 分配给哪个 p i p_i pi,即求 ∑ i = 0 ∞ [ N > > i & 1 ] 2 i A x i = S \sum_{i=0}^\infty[N>>i\&1]2^iA_{x_i}=S i=0[N>>i&1]2iAxi=S
的序列 { x } ∈ [ 1 , K ] \{x\}\in[1,K] {x}[1,K] 的方案数模 2

从低位往高位类数位DP,每次可以确定最低位,设状态 f [ i ] [ V ] f[i][V] f[i][V] 为已经确定了前 i i i 位,当前的和为 2 i ∗ V 2^i*V 2iV 的方案数,那么转移就是加入一个 A j A_j Aj。然后根据 S S S 在当前位的值确定保留哪些方案,然后整体除以2。和最大为 2 ∗ M a x A 2*MaxA 2MaxA
复杂度 O ( ( K + 1 ) ∗ M a x A ∗ log ⁡ N ) O((K+1)*MaxA*\log N) O((K+1)MaxAlogN),bitset优化至 O ( K ∗ M a x A w log ⁡ N + M a x A log ⁡ N ) O(K*\frac {MaxA}w\log N+MaxA\log N) O(KwMaxAlogN+MaxAlogN)

Code:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int T,K,A[205],Mx;
LL N,S;
bitset<200005>f,g;
int main()
{
	for(scanf("%d",&T);T--;){
		scanf("%lld%lld%d",&N,&S,&K),Mx=0;
		for(int i=1;i<=K;i++) scanf("%d",&A[i]),Mx=max(Mx,A[i]);
		f.reset(),f[0]=1;
		for(int i=0;N;N>>=1,S>>=1,i++){
			if(N&1){
				g.reset();
				for(int j=1;j<=K;j++) g^=f<<A[j];
			}
			else g=f;
			f.reset();
			for(int j=S&1;j<=2*Mx;j+=2) f[j>>1]=g[j];
		}
		printf("%d\n",int(f[S]));
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值