传送门
问题等价于从 20⋅a1,21⋅a1,⋯,259⋅a1,20⋅a2,⋯,259⋅an 共 60n 个物品中,扣除所有 2ci⋅abi 的 k 个物品后,查询体积不超过 m 的背包方案数
注意到每个 ai 前面乘的系数都是 2 的整幂次,若我们对进行背包的物品,按系数的 2 的幂次进行分类,从小到大进行背包;则在 2t 为系数的物品进行背包后,后续物品再次进行背包,是不可能影响低于 t 位的二进制数码的
为了避免状态过多,我们试图采用数位 dp 的方式,一位一位考虑对答案的贡献
注意到若进行到第 t 位后,若体积 V 的二进制低 t 位大于 m ,即 Vmod2t>mmod2t ,则当且仅当 V 的第 t+1 位为 0 且 m 的第 t+1 位为 1 会使得 Vmod2t+1≤mmod2t+1 ;若体积 V 的二进制低 t 位小于等于 m ,即 Vmod2t≤mmod2t ,则当且仅当 V 的第 t+1 位为 1 且 m 的第 t+1 位为 0 会使得 Vmod2t+1>mmod2t+1
因此,我们考虑记 dpt,i,0/1 表示进行完 2t 为系数的物品的背包后,体积 V 满足 ⌊2t+1V⌋=i ,且 Vmod2t+1 是否严格大于 mmod2t+1 ,的方案数
故答案是 i=1∑naixi≤m ,即是 V≤m 的方案数。由于 m<260 ,故 0≤⌊259+1V⌋≤⌊259+1m⌋<1,Vmod259+1≤mmod259+1 ,因此答案为 dp59,0,0
这种情况下,0≤t≤59,0≤i≤⌊2tV⌋≤2t1x=0∑ti=1∑n2x⋅ai=i=1∑nai⋅2t1x=0∑t2x≤2i=1∑nai:=2A
考虑该 dp 的转移,首先,我们需要知道系数为 2t 的物品的背包结果,它可以用生成函数 i=1(i,t)∈{(bk,ck)}∏n(1+x2t⋅ai) ,我们记为 gt(x2t) ,即 gt(x)=i=1(i,t)∈{(bk,ck)}∏n(1+xai)=i=1∏n(1+xai)⋅[(i,t)∈{(bk,ck)}∏(1+xai)]−1
我们可以先用多项式线段树分治或多项式启发式合并,先在 O(nlog2n) 的时间内求出 g(x)=i=1∏n(1+xai) 。后续求解 gt(x) 时,只需要反向跑背包进行删除,复杂度为 O(n) ;由于限制数量为 k≤5×103 个,故复杂度 O(kn) 是可接受的
现在考虑已知 dpt−1,0⋯,2A,0/1 、已知 [xl]gt(x) ,如何合并得出 dpt,0⋯2A,0/1 的结果
由于 dpt−1,i,0 表示进行完 2t−1 为系数的物品后,体积 V 满足 ⌊2tV⌋=i ,且 Vmod2t≤mmod2t 的方案数;[xl]g(x) 表示系数为 2t 的物品,拼出体积为 2t⋅l 的方案数
因此将两者合并后,新体积 V′=V+2t⋅l⇒⌊2t+1V′⌋=⌊2t+1V+2t⋅l⌋=⌊2i+l⌋ ,大小关系 b=[V′mod2t+1>mmod2t+1]=[Vt′>mt]=[⌊2tV′⌋0>mt]=[(i+l)mod2>mt]
于是可以写出一部分的转移式:dpt,⌊2I⌋,[Imod2>mt]=i+l=I∑dpt−1,i,0⋅[xl]gt(x) ,显然是加法卷积的形式,可以在 O(2Alog2A) 的时间内求解
同理,另一部分转移式为:dpt,⌊2I⌋,[Imod2≥mt]=i+l=I∑dpt−1,i,1⋅[xl]gt(x) ,同样是加法卷积,可以在 O(2Alog2A) 的时间内求解
初始时,是 dp0,i,0/1 表示进行完 20 为系数的物品后,体积 ⌊2V⌋=i 且 Vmod2 是否严格大于 mmod2 ;也是直接由 [xl]g0(x) 贡献到 dp0,⌊2l⌋,lmod2>mmod2
综上,先对 (1+xai) 进行 O(nlog2n) 的多项式启发式合并或多项式线段树分治;接着对数位进行 DP,先求出 gt(x) ,再分别利用 dpt−1,0⋯2A,0,dpt−1,0⋯2A,1 与 gt(x) 卷积递推 dpt,0⋯2A,0/1 的贡献
求 gt(x) 的总复杂度为 O(nk) ,卷积递推的复杂度为 O(logm⋅2⋅2Alog2A)=O(logm⋅AlogA)=O(logm⋅nlogn)
故总复杂度为 O(nlogn(logn+logm)+nk)
【核心代码】
const int Lim=8e4, MAXN=Lim+10;
int n, k, a[MAXN];
ll m, mask[MAXN];
poly f, g, h;
inline void divit(poly &f, int a) {
for(int i=sz(f)-1, j=i-a; j>=0; --i, --j)
f[j]=f[j]-f[i];
for(int i=0, j=a, J=sz(f); j<J; ++i, ++j)
f[i]=f[j];
rsz(f, sz(f)-a);
}
vir dp[2][MAXN][2];
inline void work() {
g=f;
for(int i=1; i<=n; ++i) if(mask[i]&1) divit(g, a[i]);
for(int i=sz(g)-1; ~i; --i) dp[0][i>>1][(i&1)>(m&1)]=dp[0][i>>1][(i&1)>(m&1)]+g[i];
for(int t=1; t<=59; ++t) {
auto lst=dp[t+1&1], now=dp[t&1];
int mt=(m>>t)&1;
for(int i=0, I=Lim; i<=I; ++i) now[i][0]=now[i][1]=0;
g=f;
for(int i=1; i<=n; ++i) if((mask[i]>>t)&1) divit(g, a[i]);
rsz(h, Lim+1);
for(int i=0; i<=Lim; ++i) h[i]=lst[i][0];
Poly::get_mul(h, g, sz(h), sz(g));
for(int i=sz(h)-1; ~i; --i)
now[i>>1][(i&1)>mt]=now[i>>1][(i&1)>mt]+h[i];
rsz(h, Lim+1);
for(int i=0; i<=Lim; ++i) h[i]=lst[i][1];
Poly::get_mul(h, g, sz(h), sz(g));
for(int i=sz(h)-1; ~i; --i)
now[i>>1][(i&1)>=mt]=now[i>>1][(i&1)>=mt]+h[i];
}
cout<<(int)dp[1][0][0];
}
inline void init() {
Poly::init();
cin>>n>>m>>k;
for(int i=1; i<=n; ++i) {
cin>>a[i];
poly &p=Poly::T[i];
rsz(p, a[i]+1);
p[0]=p[a[i]]=1;
}
f=Poly::get_merge(n);
for(int i=1, x, y; i<=k; ++i)
cin>>x>>y, mask[x]|=pw(y);
}