CF286E Ladies' Shop
Ladies' Shop
首先,给你\(n\)个数(并告诉你\(m\)),分别为\(p_{1\dots n}\)。
让你求一个数的集合,满足:
当且仅当从这个数的集合中取数(可以重复)求和时(设得到的和为\(sum\)),如果\(sum\leq m\),则数\(sum\)在给你的\(n\)个数之中。且\(n\)个数都要被组合出来。
如果没有这种集合,输出NO。
否则,先输出YES,然后输出这个集合最小时的元素个数,并输出集合中的所有元素。
\(1\leq n,m\leq 10^6,1\leq p_i\leq 10^6\)
题解
因为\(a\)中元素全部要出现,他们的组合也会出现,所以写出数集的01生成函数\(a\),并令\(a_0=1\)。
那么\(a^2\)的意义是至多选两个组合得到的。若存在\(i\le 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\n%d\n",cnt);
for(int i=1;i<=m;++i)if(b[i]==2) printf("%d ",i);
}
return 0;
}
静渊以有谋,疏通而知事。