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;
    }
    
  • 相关阅读:
    火山喷发 计蒜客16862 NOIP模拟赛 概率DP
    洛谷 1429 平面最近点对(加强版) 快排 非点分治或kdtree
    鬼脚图 计蒜客17353 NOIP模拟 归并排序逆序对
    小X的佛光 NOIP模拟赛 倍增LCA 树结构
    小X的质数 NOIP模拟赛 魔改线性筛素数
    Win7Office2010Flash控件无法使用"此演示文稿中一些控件无法激活,可能这些控件未在此计算机中注册"
    【NOILinux】VmWare15使用技巧
    【超链接】导航网站
    C++统计博客园写过的代码行数
    合并多个txt文件到一个
  • 原文地址:https://www.cnblogs.com/autoint/p/11265969.html
Copyright © 2011-2022 走看看