题解 高维游走
感觉是个很巧妙的优化方式
首先发现模数是 2,由 Lucas 定理知此时 \(\binom{n}{k}=1\) 仅当 \(k\) 是 \(n\) 的子集
于是可以过 subtask 1
令在每个位置选的数的数量为 \(x_i\)
则在 \(t_0\) 处的选法数是 \(\binom{t_0}{x_1\ x_2\ x_3\ \cdots\ x_m}\),注意这里不是组合数(我赛时挂在这里了)
尝试找些性质:
发现 \(\{x_1, x_2, x_3,\cdots,x_m, t_0-\sum x_i\}\) 是 \(t_0\) 的一个二进制划分
这个证明可以归纳证,因为 \(x_1\) 一定是 \(t_0\) 的子集,所以 \(x_2\) 一定是 \(t_0-x_1\) 的子集,以此类推
于是有个做法是对每一位做背包
构造一个 \(31\times (m+1)\) 的 01 矩阵 \(g\),其中 \(g_{i, j}=1\) 仅当 \(j=1\) 或 \(t_0\) 和 \(t_{j-1}\) 的第 \(i-1\) 位都是 1
我们需要在每行选一个 1,若在位置 \(j\) 选则对体积的贡献为 \(2^{i-1}(j-1)\)
于是就可以做背包了
这样可以过 subtask 2
发现对 2 取模等价于异或,所以可以 bitset 优化
可以过 subtask 3
再优化需要找些性质了:
发现这个背包的转移很有特点:\(f_{i, j}\gets f_{i, j}+f_{i-1, j-k2^i}\)
这里 \(k\in [0, m]\)
就可以发现 \(j\) 按照对 \(2^i\) 的余数分成了若干组
虽然组数有很多,但本质不同的组数只有 \(2^m\) 种
于是可以想到对这个东西做 DP 套 DP
令 \(r\equiv j\pmod{2^i}\)
那么每组形如 \(\{g_{i,r}\ , g_{i, 2^i+r}\ , g_{i, 2\times2^i+r}\ ,\cdots\}\)
考虑状压枚举一组的状态
对于第 \(i\) 层,状态为 \(s\) 的一组,可以先用 \(s\) 做背包
这样我们得到了 \(k\in [0, 2m]\) 内的一个状态,这时是 \(k2^i\)
我们需要将模数改为 \(2^{i+1}\)
简单写一下:
于是就可以将这个 \([0, 2m]\) 内的状态分成两个模 \(2^{i+1}\) 意义下的状态,就可以转移了
复杂度 \(O(Tm2^m\log t)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int m;
int t[N];
namespace check{
const int mod=1e9+7;
int fac[N], inv[N];
inline int C(int n, int k) {return n<k?0:fac[n]*inv[k]%mod*inv[n-k]%mod;}
inline int lucas(int n, int k) {return !k?1:lucas(n/mod, k/mod)*C(n%mod, k%mod)%mod;}
void solve() {
fac[0]=fac[1]=1; inv[0]=inv[1]=1;
for (int i=2; i<=1000; ++i) fac[i]=fac[i-1]*i%mod;
for (int i=2; i<=1000; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for (int i=2; i<=1000; ++i) inv[i]=inv[i-1]*inv[i]%mod;
for (int i=1; i<=20; ++i) cout<<(C(i, 3)&1)<<' '; cout<<endl;
}
}
namespace table{
int fac[N], inv[N];
inline int C(int n, int k) {return n<k?0:fac[n]*inv[k]%2*inv[n-k]%2;}
inline int lucas(int n, int k) {return !k?1:lucas(n/2, k/2)*C(n%2, k%2)%2;}
void solve() {
fac[0]=fac[1]=1; inv[0]=inv[1]=1;
for (int i=2; i<=1000; ++i) fac[i]=fac[i-1]*i%2;
for (int i=2; i<=1000; ++i) inv[i]=(2-2/i)*inv[2%i]%2;
for (int i=2; i<=1000; ++i) inv[i]=inv[i-1]*inv[i]%2;
for (int i=1; i<=20; ++i) cout<<lucas(i, 3)<<' '; cout<<endl;
for (int i=1; i<=20; ++i) cout<<(((3&i)==3)?1:0)<<' '; cout<<endl;
}
}
namespace task1{
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a,b>>=1) if (b&1) ans=ans*a; return ans;}
void solve() {cout<<qpow(2, __builtin_popcount(t[0]&t[1]))<<endl;}
}
namespace task2{
int f[11][41][220];
void solve() {
// int s0=13;
// cout<<"s0: "<<bitset<10>(s0)<<endl;
// for (int s=s0; s; s=(s-1)&s0) {
// cout<<bitset<10>(s)<<endl;
// }
int lim=t[0], sum=0;
for (int i=1; i<=m; ++i) sum+=t[i];
lim=min(t[0], sum);
memset(f, 0, sizeof(f));
f[0][0][0]=1;
for (int i=1; i<=m; ++i) {
for (int s=0; s<=t[i]; ++s) if ((s&(t[i]))==s) {
for (int j=lim; j>=s; --j)
for (int k=i*s; k<220; ++k)
f[i][j][k]+=f[i-1][j-s][k-i*s];
}
}
int ans=0;
for (int i=0; i<220; ++i) {
int sum=0;
for (int j=0; j<=lim; ++j) if ((j&t[0])==j) sum+=f[m][j][i];
ans+=(sum&1);
}
cout<<ans<<endl;
}
}
namespace task3{
bool mp[35][15];
int f[35][10000];
void solve() {
memset(mp, 0, sizeof(mp));
memset(f, 0, sizeof(f));
for (int i=1; i<=31; ++i)
for (int j=1; j<=m+1; ++j)
mp[i][j]=(j==1)||((t[0]&(1<<(i-1)))&&(t[j-1]&(1<<(i-1))));
f[0][0]=1;
for (int i=1; i<=31; ++i) {
for (int j=1; j<=m+1; ++j) if (mp[i][j]) {
int dlt=((j-1)<<(i-1));
for (int k=dlt; k<10000; ++k)
f[i][k]=(f[i][k]+f[i-1][k-dlt])&1;
}
}
int ans=0;
for (int i=0; i<10000; ++i) if (f[31][i]&1) ++ans;
cout<<ans<<endl;
}
}
namespace task{
int f[2][1<<11], now;
void solve() {
memset(f[now], 0, sizeof(f[now]));
f[now][1]=1;
int lim=1<<(m+1), ans=0;
for (int i=0; i<=31; ++i,now^=1) {
memset(f[now^1], 0, sizeof(f[now^1]));
int mp=1;
for (int j=1; j<=m; ++j) if ((t[0]&t[j]&(1<<i))) mp|=1<<j;
for (int s=0; s<lim; ++s) if (f[now][s]) {
int mask=0, half[2]={0, 0};
for (int j=0; j<=m; ++j) if (mp&(1<<j)) mask^=(s<<j);
for (int j=2*m; ~j; --j) half[j&1]=(half[j&1]<<1)|((mask>>j)&1);
f[now^1][half[0]]+=f[now][s];
f[now^1][half[1]]+=f[now][s];
}
}
for (int s=0; s<lim; ++s) ans+=f[now][s]*__builtin_popcount(s);
cout<<ans<<endl;
}
}
signed main()
{
freopen("travel.in", "r", stdin);
freopen("travel.out", "w", stdout);
int T=read();
while (T--) {
m=read();
for (int i=0; i<=m; ++i) t[i]=read();
// if (m==1) task1::solve();
// else task2::solve();
// task2::solve();
task::solve();
}
// check::solve();
// table::solve();
// task1::solve();
// task2::solve();
return 0;
}