多项式套路入门 (2) - 背包问题

无限背包

这类问题的经典形式:给定一些物品的体积,问用这些物品可以拼出某个范围内的哪些体积。

对于这种问题,考虑构造多项式 \(F_{i}\)。设物品集合为 \(S\),则 \(F_{i}=[i\in S]\),如果物品范围不是从 \(0\) 开始,我们还可以添加一个体积为 \(0\) 的物品。然后构造 \(G=F*F\),则如果 \(G_{i}>0\),那么 \(i\) 可以通过至多 \(2\) 个体积值相加得到。如果 \(G_{i}>0\),使得 \(G_{i}=1\),再构造 \(H=G*G\),则如果 \(H_{i}>0\),那么 \(i\) 可以通过至多 \(4\) 个体积值相加得到。平方 \(q\) 次则得到至多 \(2^{q}\) 个物品体积相加可得的所有体积。时间复杂度 \(O(mlog^2m)\)\(m\) 为值域(即体积范围)。

例题:[CF286E]Ladies' Shop

题目给出两个限制:

\(1\)、选出的数构成的集合可以拼出序列 \(a\) 中的所有数。

2、选出的数构成的集合拼出了一个数 \(x\)\(x\leq m\),则 \(x\) 一定是序列 \(a\) 中的一个数。

根据这两个条件,我们显然得到无解的条件:对于 \(\forall i \in [1,m]\),设 \(S=\{a_{k}\}(1\leq k \leq n)\),则如果 \(i \notin S\) 并且 \(i=a_{p}+a_{q}(1\leq p,q\leq n)\),则无解。因为不满足限制 \((2)\)

现在考虑有解的情况。设选出的数集为 \(Q\),如果存在 \(Q_{i}=Q_{j}+Q_{k}\),则我们可以考虑不选择 \(Q_{i}\) 而只选择 \(Q_{j}\)\(Q_{k}\),因为 \(Q_{i}\) 可以被拼出,则此时我们可以把 \(Q_{j}+Q_{k}\) 看成是一个数。所以如果存在 \(i\in S\) 并且 \(i=a_{p}+a_{q}(1\leq p,q \leq n)\),则我们不选 \(i\) 这个数。我们的目标转换为求所有满足以上条件的 \(i\)

考虑设计函数 \(f_{i}\) 表示 \(i\) 能否被拆成序列 \(a\) 中的两数之和。即 \(f_{i}=[\exists j\in [1,i-1] ,j\in S,(i-j)\in S]\),发现 \(a_{i}>0\),则有 \(f_{i}=[\exists j\in [0,i] ,j\in S,(i-j)\in S]\),表示如果 \(f_{i}=1\),则 \(f_{i}\) 能被拆成序列 \(a\) 中的两数之和。发现这个函数非常没用,因为函数之间一点联系都没有......考虑乘法的运算方式,\(1\times 1=1\)\(0\times 1=1\times 0=0\times 0\)。所以我们改 \(f_{i}\) 表示 \(i\) 被拆成序列 \(a\) 中的两数之和的方案数,得到:

\[\qquad f_{i}=\sum\limits_{j=0}^{i}[j\in S][(i-j)\in S] \qquad \]

观察上面的式子,有点像卷积的形式。我们考虑再构造出一个函数 \(g_{x}=[x\in S]\),则显然的,\(f_{i}=\sum\limits_{j=0}^{i}g_{j}\times g_{i-j}=\sum\limits_{p+q=i}g_{p}\times g_{q}\)

这东西就是卷积形式了。再构造 \(F_{x}=\sum\limits_{j=0}^{M}g_{j}\times x^{i}\)\(G_{x}=\sum\limits_{j=0}^{M}f_{j}\times x^{i}\),则由上式,得到 \(G=F*F\)

直接 \(\text{FFT}\) 或者 \(\text{NTT}\) 即可解决。最后如果 \(G_{i}=0\) 并且 \(i\in S\),则这个数我们要选中,否则我们不选。

#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <bitset>
#define ri register
#define inf 0x7fffffff
#define E (1)
#define mk make_pair
//#define int long long
//#define double long double
using namespace std; const int N=4000010, Mod=998244353;
inline int read()
{
    int s=0, w=1; ri char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch-'0'), ch=getchar();
    return s*w;
}
void print(int x) { if(x<0) x=-x, putchar('-'); if(x>9) print(x/10); putchar(x%10+'0'); }
int n,m,a[N],g[N],f[25][2],book[N],T,rev[N];
inline void Get_Rev() { for(ri int i=0;i<T;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(T>>1):0); }
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
inline void NTT(int *s,int type)
{
    for(ri int i=0;i<T;i++) if(i<rev[i]) swap(s[i],s[rev[i]]);
    for(ri int i=2,cnt=1;i<=T;cnt++, i<<=1)
    {
        int wn=f[cnt][type];
        for(ri int j=0, mid=(i>>1);j<T;j+=i)
        {
            for(ri int k=0, w=1;k<mid;k++, w=1ll*w*wn%Mod)
            {
                int x=s[j+k], y=1ll*w*s[j+mid+k]%Mod;
                s[j+k]=(x+y)%Mod, s[j+mid+k]=(x-y+Mod)%Mod;
            }
        }
    }
    if(!type) for(ri int i=0,inv=ksc(T,Mod-2);i<T;i++) s[i]=1ll*s[i]*inv%Mod;
}
signed main()
{
    f[23][1]=ksc(3,119), f[23][0]=ksc(332748118,119);
    for(ri int i=22;~i;i--) f[i][1]=1ll*f[i+1][1]*f[i+1][1]%Mod, f[i][0]=1ll*f[i+1][0]*f[i+1][0]%Mod;
    n=read(), m=read();
    for(ri int i=1;i<=n;i++) a[i]=read(), g[a[i]]=book[a[i]]=1;
    T=1; while(T<=m+m) T<<=1; Get_Rev();
    NTT(g,1);
    for(ri int i=0;i<T;i++) g[i]=1ll*g[i]*g[i]%Mod;
    NTT(g,0);
    for(ri int i=1;i<=m;i++) if(g[i] && !book[i]) { puts("NO"); return 0; }
    puts("YES"); int cnt=0;
    for(ri int i=1;i<=n;i++) if(!g[a[i]]) cnt++;
    printf("%d\n",cnt);
    for(ri int i=1;i<=n;i++) if(!g[a[i]]) printf("%d ",a[i]);
    puts("");
    return 0;
}
posted @ 2020-08-05 15:42  zkdxl  阅读(350)  评论(1编辑  收藏  举报