zoukankan      html  css  js  c++  java
  • CF286E Ladies' Shop

    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;
    }
    
  • 相关阅读:
    使用Beanstalkd_console
    使用Beanstalkd实现队列
    队列中使用Database Driver
    Myeclipse中无法删除部署在tomcat上的工程
    欢迎使用CSDN-markdown编辑器
    oracle11g 远程登录数据库
    oracle11g卸载出错 无法删除文件,文件正在使用中
    oracle11g OEM无法连接到数据库实例解决办法
    用SQLData读写数据库自定义类型
    java读写中文文件
  • 原文地址:https://www.cnblogs.com/autoint/p/11265969.html
Copyright © 2011-2022 走看看