codeforces 286E Ladies' Shop

题目大意:n个小于等于m的数,现在你需要在[1,m]中选择若干个数,使得选出的数能组成的所有数正好与n个数相同,给出最少要选多少个数。

题目分析:

结论一:选择的若干个数一定在n个数中。

证明:否则的话不满足“正好”。

结论二:若a,b在由n个数组成的集合中,则a+b(a+b<=m)也在由n个数组成的集合中。

证明:通过归纳法可以证明。

那么我们考虑构造生成函数G(x)=∑ki*xi,其中当由n组成的集合中有数i时ki=1,否则为0。接着将多出的数删除即可。

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const ll mod = 998244353;
const int gg = 3;
const int maxn = 2520210;
int mp[maxn],a[maxn],f[maxn];
int n,m;
int ord[maxn];

int fast_pow(int now,int p){
    if(p == 0) return 1;
    if(p == 1) return now;
    int z = fast_pow(now,p/2); z = (1ll*z*z)%mod;
    if(p & 1){z = (1ll*z*now)%mod;}
    return z;
}

void fft(int *d,int len,int kind){
    for(int i=0;i<len;i++) if(ord[i] > i) swap(d[i],d[ord[i]]);
    for(int i=1;i<len;i<<=1){
        int wn = fast_pow(gg,(mod-1)/(i<<1));
        if(kind == -1) wn = fast_pow(wn,mod-2);
        for(int j=0;j<len;j += (i<<1)){
            int w = 1;
            for(int k=0;k<i;k++,w=(1ll*w*wn)%mod){
                ll x = d[j+k],y = (1ll*w*d[j+k+i])%mod;
                d[j+k] = (x+y)%mod; d[j+k+i] = (x-y+mod)%mod;
            }
        }
    }
    if(kind == -1){
        int inv = fast_pow(len,mod-2);
        for(int i=0;i<len;i++) d[i] = (1ll*d[i]*inv)%mod;
    }
}

void read(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),mp[a[i]] = f[a[i]] = 1;
}

void work(){
    int bit = 0,len = 1;
    while(len < m*2) bit++,len<<=1;
    for(int i=1;i<len;i++) ord[i] = (ord[i>>1]>>1) + ((i&1)<<bit-1);
    fft(f,len,1);
    for(int i=0;i<len;i++) f[i] = (1ll*f[i]*f[i])%mod;
    fft(f,len,-1);
    int ans = 0;
    for(int i=0;i<=m;i++){
        if(f[i]&&mp[i]) {mp[i] = 0;continue;}
        if(f[i]){puts("NO");return;}
        if(mp[i]) ans++;
    }
    puts("YES");
    printf("%d
",ans);
    for(int i=0;i<=m;i++) if(mp[i]) printf("%d ",i);
}

int main(){
    read();
    work();
    return 0;
}
原文地址:https://www.cnblogs.com/1-1-1-1/p/8572193.html