zoukankan      html  css  js  c++  java
  • CodeForces 286E Ladies' Shop 多项式 FFT

    原文链接http://www.cnblogs.com/zhouzhendong/p/8781889.html

    题目传送门 - CodeForces 286E

    题意

      首先,给你$n$个数(并告诉你$m$),分别为$p_{1dots n}$。

      让你求一个数的集合,满足:

        当且仅从这个数的集合中取数(可以重复)求和时(设得到的和为$sum$),如果$sumleq m$,则数$sum$在给你的$n$个数之中。

      如果没有这种集合,输出$NO$。

      否则,先输出$YES$,然后输出这个集合最小时的元素个数,并输出集合中的所有元素。

      $1leq n,mleq 10^6,1leq p_ileq 10^6$

    题解

      大大的标签里面写着$FFT$。

      然而我只会$O(m log^2 m)$。QAQ

      瞅了眼题解。发现我果然是大菜鸡。

     

      好了开始讲算法。

      首先,我们构造一个多项式:

      $$f=1x^0+a_1x^1+a_2x^2+dots+a_ix^i+dots+a_{m-1}x^{m-1}+a_mx^m$$

      其中,如果数$i$在告诉你的$n$个数中出现过,那么$a_i=1$,否则$a_i=0$。

      然后我们$FFT$求得$f^2$。

      有什么用??

      当然有用。(这大概是个比较好的套路)

      设$f^2$的$i$次项系数为$b_i$,那么,如果$b_i>0$则可以通过给你的$n$个数来合成$i$。

      那为什么我们先要让给你的$n$个数的$a_i$都变成$1$呢,显然给你的$n$个数一定会被合成,也一定会被用于合成其他的数。

      那为什么$a_0=1$呢?显然一个数加上$0$是不变的,弄个$0$上去可以保留原来有的$n$个数。

      于是你得到了从原来的$n$个数中拿出$0$~$2$个的结果。

      然而最多可能拿$m$个。

      所以你还要继续,用快速幂求得$f^m$。

      时间复杂度$O(mlog^2 m)$。

      事实上我傻掉了。

      显然$a_i>0$的,$b_i$也$>0$,由于$0$的存在。

      那么我们只要保证$a_i=0,b_i>0,ileq m$的不存在就好了。

      如果第一轮不存在这些不合法的。

      那么显然后面也不可能。因为系数不为$0$的仍然是那些原来的$i$。

      于是只要一次$FFT$。

      然后考虑一下哪些数可以省略。

      显然,结果中$b_i$有两次贡献是$0$与$a_i$做的,如果$b_i$大于$2$说明$i$可以通过其他的两个数相加得到,所以$i$可以不在结果的集合里。反之,如果$b_i=2$,那么显然一定在结果的集合里面。

      然后就放代码了。注意我代码里面的$n$的意义不是输入的$n$。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1<<21;
    double PI=acos(-1.0);
    int read(){
    	int x=0;
    	char ch=getchar();
    	while (!('0'<=ch&&ch<='9'))
    		ch=getchar();
    	while ('0'<=ch&&ch<='9')
    		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return x;
    }
    int nn,m,a[N],b[N];
    int n,L,R[N];
    struct C{
    	double r,i;
    	C(){}
    	C(double a,double b){r=a,i=b;}
    	C operator + (C x){return C(r+x.r,i+x.i);}
    	C operator - (C x){return C(r-x.r,i-x.i);}
    	C operator * (C x){return C(r*x.r-i*x.i,r*x.i+i*x.r);}
    }A[N],B[N],CC[N],w[N];
    double x[N],y[N],z[N];
    void FFT(C a[]){
    	for (int i=0;i<n;i++)
    		if (i>R[i])
    			swap(a[i],a[R[i]]);
    	for (int t=n>>1,d=1;d<n;d<<=1,t>>=1)
    		for (int i=0;i<n;i+=(d<<1))
    			for (int j=0;j<d;j++){
    				C tmp=w[t*j]*a[i+j+d];
    				a[i+j+d]=a[i+j]-tmp;
    				a[i+j]=a[i+j]+tmp;
    			}
    }
    void FFT_times(double x[],double y[],double z[]){
    	for (int i=0;i<n;i++)
    		A[i]=C(x[i],0),B[i]=C(y[i],0);
    	FFT(A),FFT(B);
    	for (int i=0;i<n;i++)
    		CC[i]=A[i]*B[i],w[i].i*=-1.0;
    	FFT(CC);
    	for (int i=0;i<n;i++)
    		z[i]=CC[i].r/n,w[i].i*=-1.0;
    }
    int main(){
    	nn=read(),m=read();
    	memset(a,0,sizeof a);
    	a[0]=1;
    	for (int i=1,x;i<=nn;i++)
    		a[read()]=1;
    	for (n=1,L=0;n<(m+1)*2;n<<=1,L++);
    	for (int i=0;i<n;i++){
    		R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    		w[i]=C(cos(2*i*PI/n),sin(2*i*PI/n));
    	}
    	for (int i=0;i<n;i++)
    		x[i]=y[i]=i<=m?a[i]:0;
    	FFT_times(x,y,z);
    	for (int i=1;i<=m;i++)
    		b[i]=(int)(z[i]+0.5);
    	int flag=0;
    	for (int i=1;i<=m;i++)
    		if (!a[i]&&b[i]){
    			flag=-1;
    			break;
    		}
    		else if (a[i]&&b[i]==2)
    			flag++;
    	if (flag==-1)
    		puts("NO");
    	else {
    		puts("YES");
    		printf("%d
    ",flag);
    		for (int i=1;i<=m;i++)
    			if (a[i]&&b[i]==2)
    				printf("%d ",i);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    js数组操作
    docker操作命令
    swoole使用案例
    swoole实现视频弹幕效果
    swoole的UDP服务
    swoole的TCP服务
    安装回环网卡&安装Linux前准备
    Linux之安装Linux详细步骤
    Spring Boot的面试题
    Shell 脚本面试问题大全
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF286E.html
Copyright © 2011-2022 走看看