题目大意: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; }