Ladies' Shop
首先,给你(n)个数(并告诉你(m)),分别为(p_{1dots n})。
让你求一个数的集合,满足:
当且仅当从这个数的集合中取数(可以重复)求和时(设得到的和为(sum)),如果(sumleq m),则数(sum)在给你的(n)个数之中。且(n)个数都要被组合出来。
如果没有这种集合,输出NO。
否则,先输出YES,然后输出这个集合最小时的元素个数,并输出集合中的所有元素。
(1leq n,mleq 10^6,1leq p_ileq 10^6)
题解
因为(a)中元素全部要出现,他们的组合也会出现,所以写出数集的01生成函数(a),并令(a_0=1)。
那么(a^2)的意义是至多选两个组合得到的。若存在(ile m,a_i=0,a^2_i>0),则说明组合出的元素不合题意,即无解。若不存在这样的情况,那么(a)的任意正整数次方都是一样的。
那么有解的情况下如何判断最少选多少呢?考虑若(i)不能被其他元素组合,则(a^2_i=2),即(i)和(0)组合的方案数。若(a^2_i>2),则说明(i)能被其他元素组合出来。那么去掉这些多余的即可。
使用FFT优化多项式乘法,时间复杂度(O(n log n))。
struct node {double x,y;};
il node operator+(co node&a,co node&b){
return (node){a.x+b.x,a.y+b.y};
}
il node operator-(co node&a,co node&b){
return (node){a.x-b.x,a.y-b.y};
}
il node operator*(co node&a,co node&b){
return (node){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
}
il node operator/(co node&a,double k){
return (node){a.x/k,a.y/k};
}
co double pi=acos(-1);
co int N=1<<21;
int n,m,a[N],b[N];
int len,lim,rev[N];
node c[N];
void fourier_trans(node a[],int inv){
for(int i=0;i<lim;++i)if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int step=1;step<lim;step<<=1){
double alpha=inv*pi/step;
for(int k=0;k<step;++k){
node omega=(node){cos(alpha*k),sin(alpha*k)};
for(int even=k;even<lim;even+=step<<1){
int odd=even+step;node t=omega*a[odd];
a[odd]=a[even]-t,a[even]=a[even]+t;
}
}
}
if(inv==-1)for(int i=0;i<lim;++i) a[i]=a[i]/lim;
}
int main(){
read(n),read(m);
a[0]=1;
for(int i=1;i<=n;++i) a[read<int>()]=1;
len=ceil(log2(2*m+1)),lim=1<<len;
for(int i=0;i<lim;++i){
rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
c[i]=(node){i<=m?a[i]:0,0};
}
fourier_trans(c,1);
for(int i=0;i<lim;++i) c[i]=c[i]*c[i];
fourier_trans(c,-1);
for(int i=1;i<=m;++i) b[i]=round(c[i].x);
bool valid=1;int cnt=0;
for(int i=1;i<=m;++i){
if(!a[i]&&b[i]) {valid=0;break;}
else if(b[i]==2) ++cnt;
}
if(!valid) puts("NO");
else{
printf("YES
%d
",cnt);
for(int i=1;i<=m;++i)if(b[i]==2) printf("%d ",i);
}
return 0;
}