CF 632E

本题是一道好题...

首先我们可以看到,本题其实可以用完全背包跑,但是复杂度不对

所以我们考虑优化:

我们知道,如果有三个物品价值分别为a_i,a_j,a_k

如果允许取一个物品,那么a_i,a_j,a_k都是合法的答案

如果允许取三个物品,那么a_i+a_j+a_k就是一个合法的价值(废话)

这是否给了我们一些启示呢?

如果我们设集合S=\begin{Bmatrix} a_1,& a_2 ,&...&a_n \end{Bmatrix},构造一个多项式A(x)=\sum_{i=0}^{max(a_i)}(i\in S?1:0)x^i

可以发现,如果k=1,那么这个多项式中每个x^i项前系数不为0的i即为合法答案

如果k=2,那么这个多项式自乘一次,每个x^i项前系数不为0的即为合法答案

(这一点很好理解,一个x^i项前系数不为0的条件是当且仅当至少存在一对j,k使得在原多项式中x^j,x^k前系数均不为0,且j+k=i,而根据定义,如果x^j,x^k前系数不为0,说明j\in S,k\in S,那么也就是选出了两件物品嘛)

因此,对于任意的k,我们只需要将构造出的多项式自乘k次,然后找出系数不为0的项,输出答案即可

接下来谈几个细节:

首先,本题数据范围过大,因此FFT是难以通过的,建议使用NTT

其次,NTT的常用模数会被卡(比如998244353和1004535809),因此需要用一些不常见的东西,比如469762049(原根为3),这个东西亲测不会卡(如果你非要用前两个,请使用双模)

然后就结束了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
const double pi=acos(-1.0);
//const ll mode1=998244353;
const ll mode=469762049;
int to[(1<<21)+5];
int n,lim=1,l;
int m,k;
ll pow_mul(ll x,ll y)
{
    ll ans=1;
    while(y)
    {
        if(y&1)ans=ans*x%mode;
        x=x*x%mode,y>>=1;
    }
    return ans;
}
void NTT(ll *a,int len,int k)
{
    for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);
    for(int i=1;i<len;i<<=1)
    {
        ll w0=pow_mul(3,(mode-1)/(i<<1));
        for(int j=0;j<len;j+=(i<<1))
        {
            ll w=1;
            for(int o=0;o<i;o++,w=w*w0%mode)
            {
                ll w1=a[j+o],w2=a[j+o+i]*w%mode;
                a[j+o]=(w1+w2)%mode,a[j+o+i]=((w1-w2)%mode+mode)%mode;
            }
        }
    }
    if(k==-1)for(int i=1;i<(len>>1);i++)swap(a[i],a[len-i]);
}
ll a[(1<<21)+5],b[(1<<21)+5],c[(1<<21)+5];
ll temp[(1<<21)+5];
int n1,n2;
void pow_mul()
{
    b[0]=1;
    while(k)
    {
        if(k&1)
        {
            lim=1,l=0;
            while(lim<=2*max(n1,n2))lim<<=1,l++;
            for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(l-1)));
            for(int i=0;i<lim;i++)temp[i]=a[i];
            NTT(b,lim,1),NTT(temp,lim,1);
            for(int i=0;i<lim;i++)c[i]=temp[i]*b[i]%mode;
            NTT(c,lim,-1);
            ll inv=pow_mul(lim,mode-2);
            for(int i=0;i<lim;i++)b[i]=c[i]*inv%mode,c[i]=temp[i]=0;
            n2+=n1;
        }
        lim=1,l=0;
        while(lim<=2*n1)lim<<=1,l++;
        for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(l-1)));
        NTT(a,lim,1);
        for(int i=0;i<lim;i++)c[i]=a[i]*a[i]%mode;
        NTT(c,lim,-1);
        ll inv=pow_mul(lim,mode-2);
        for(int i=0;i<lim;i++)a[i]=c[i]*inv%mode,c[i]=0;
        k>>=1;
        n1<<=1;
    }
    for(int i=0;i<n2;i++)if(b[i]>0)printf("%d ",i);
    printf("\n");
}
int main()
{
    scanf("%d%d",&m,&k);
    n1=1000,n2=1000;
    for(int i=1;i<=m;i++)
    {
        int t;
        scanf("%d",&t);
        a[t]=1;
    }
    pow_mul();
    return 0;
}

 

posted @ 2019-05-03 17:02  lleozhang  Views(318)  Comments(0Edit  收藏  举报
levels of contents