题目描述:
给出 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 K≤200,N,S≤1018,0≤Ai≤105
题目分析:
设每个 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=0∑∞⌊2iN⌋−j=1∑K⌊2ipj⌋
而
∑
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
2i∗V 的方案数,那么转移就是加入一个
A
j
A_j
Aj。然后根据
S
S
S 在当前位的值确定保留哪些方案,然后整体除以2。和最大为
2
∗
M
a
x
A
2*MaxA
2∗MaxA
复杂度
O
(
(
K
+
1
)
∗
M
a
x
A
∗
log
N
)
O((K+1)*MaxA*\log N)
O((K+1)∗MaxA∗logN),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(K∗wMaxAlogN+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]));
}
}