zoukankan      html  css  js  c++  java
  • [JSOI2012][bzoj4332] 分零食 [FFT]

    题面

    传送门

    思路

    首先,这个数据如果没有这么大,我们还是可以做朋友的......

    设$dpleft[i ight]left[j ight]$代表前j个零食分给了前i个人的方案数

    那么dp方程显然:

    $dpleft[i ight]left[j ight]=sum_{k=1}^{j-1} dpleft[i-1 ight]left[k ight]+fleft(j-k ight)$

    其中$fleft(x ight)$就是题目里给的那个二次函数

    同时有一个性质:

    $dpleft[i ight]left[j ight]=dpleft[frac i2 ight]left[k ight]ast dpleft[frac i2 ight]left[j-k ight]$

    显然这道题不能直接O(nm)递推......那我们换个办法来想

    n辣么大,为什么我们不考虑 一下用倍增的方法呢?正好上面那个性质可以利用一下

    并且还应当注意,我们最后要求的是$sum_{i=1}^n dpleft[i ight]left[m ight]$

    所以我们设$pleft[i ight]left[j ight]=sum_{k=1}^n dpleft[k ight]left[j ight]$

    $pleft[i ight]left[j ight]=pleft[frac i2 ight]left[j ight]+sum_{k=1}^{frac i2}dpleft[k+frac i2 ight]left[j ight]$

    $pleft[i ight]left[j ight]=pleft[frac i2 ight]left[j ight]+sum_{k=1}^{frac i2}sum_{l=1}^{j-1}dpleft[k ight]left[l ight]dpleft[frac i2 ight]left[j-l ight]$

    $pleft[i ight]left[j ight]=pleft[frac i2 ight]left[j ight]+sum_{l=1}{j-1}sum_{k=1}{frac i2}dpleft[k ight]left[l ight]dpleft[frac i2 ight]left[j-l ight]$

    $pleft[i ight]left[j ight]=pleft[frac i2 ight]left[j ight]+sum_{l=1}^{j-1}dpleft[frac i2 ight]left[j-l ight]sum_{k=1}^{frac i2}dpleft[k ight]left[l ight]$

    $pleft[i ight]left[j ight]=pleft[frac i2 ight]left[j ight]+sum_{l=1}^{j-1}dpleft[frac i2 ight]left[j-l ight]pleft[frac i2 ight]left[l ight]$

    也就是说p可以由上一层的p加上一层的dp与p的卷积得到,而dp可以由上一层的dp自乘得到

    那么自然可以用倍增p的第一层参数的方法,用FFT优化一下,一直做到n

    时间效率为$Oleft(mlogmlogn ight)$

    注意:将n转化为二进制,那么为一的那些位,要在倍增完以后再推一层

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    inline int read(){
    	int re=0,flag=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){
    		if(ch=='-') flag=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    	return re*flag;
    }
    struct complex{
    	double x,y;
    	complex(double xx=0,double yy=0){x=xx;y=yy;}
    	complex operator +(const complex &b){return complex(x+b.x,y+b.y);}
    	complex operator -(const complex &b){return complex(x-b.x,y-b.y);}
    	complex operator *(const complex &b){return complex(x*b.x-y*b.y,x*b.y+y*b.x);}
    }A[100010],B[100010];
    const double pi=acos(-1.0);
    int n,m,limit=1,cnt=0,r[100010];
    int MOD;
    void fft(complex *a,double type){
    	int i,j,k,mid;complex x,y,wn,w;
    	for(i=0;i<limit;i++) if(i<r[i]) swap(a[i],a[r[i]]);
    	for(mid=1;mid<limit;mid<<=1ll){
    		wn=complex(cos(pi/mid),type*sin(pi/mid));
    		for(j=0;j<limit;j+=(mid<<1ll)){
    			w=complex(1,0);
    			for(k=0;k<mid;k++,w=w*wn){
    				x=a[j+k];y=a[j+k+mid]*w;
    				a[j+k]=x+y;a[j+k+mid]=x-y;
    			}
    		}
    	}
    }
    int now=1,w=0,g[100010]={0},p[100010]={0},f[100010]={0};
    int a1,a2,a3;
    void solve1(){
    	int i;
    	for(i=0;i<=limit;i++) A[i]=B[i]=complex(0,0);
    	for(i=0;i<=limit;i++) A[i].x=p[i],B[i].x=g[i];
    	fft(A,1);fft(B,1);
    	for(i=0;i<=limit;i++) A[i]=A[i]*B[i];
    	fft(A,-1);
    	for(i=1;i<=m;i++) p[i]=(p[i]+(int)(A[i].x/limit+0.5)%MOD)%MOD;
    	
    	for(i=0;i<=limit;i++) A[i]=complex(0,0);
    	for(i=0;i<=limit;i++) A[i].x=g[i];
    	fft(A,1);
    	for(i=0;i<=limit;i++) A[i]=A[i]*A[i];
    	fft(A,-1);
    	for(i=1;i<=m;i++) g[i]=(int)(A[i].x/limit+0.5)%MOD;
    }
    void solve2(){
    	int i;
    	for(i=0;i<=limit;i++) A[i]=B[i]=complex(0,0);
    	for(i=1;i<=m;i++) A[i].x=f[i],B[i].x=g[i];
    	fft(A,1);fft(B,1);
    	for(i=0;i<=limit;i++) A[i]=A[i]*B[i];
    	fft(A,-1);
    	for(i=1;i<=m;i++) g[i]=(int)(A[i].x/limit+0.5)%MOD,p[i]=(p[i]+g[i])%MOD;
    }
    int main(){
    	m=read();MOD=read();n=read();a1=read();a2=read();a3=read();
    	int i;
    	a1%=MOD;a2%=MOD;a3%=MOD;
    	for(i=1;i<=m;i++) g[i]=p[i]=f[i]=((((((a1*i)%MOD)*i)%MOD)+a2*i%MOD)+a3)%MOD;
    	while(limit<=(m<<1ll)) limit<<=1ll,cnt++;
    	for(i=0;i<limit;i++) r[i]=((r[i>>1ll]>>1ll)|((i&1ll)<<(cnt-1ll)));
    	while((now<<1ll)<=n) now<<=1ll,w++;
    	while(w){
    		w--;
    		solve1();//倍增
    		if(n&(1<<w)) solve2();//这一位应该是个奇数的,再推一层
    	}
    	printf("%lld
    ",p[m]%MOD);
    }
    
  • 相关阅读:
    什么造就一个伟大的站点
    我的一些关于商业计划书的经验
    iPhone开发:万能的NSData
    两种快速打乱NSMutableArray的方法
    交大校友:互联网大佬们
    程序员的十层楼 11层(上帝)
    iPhone开发:使用NSValue存储任意类型的数据
    Linux之lsof命令
    MySQL密码忘了怎么办?MySQL重置root密码方法
    nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/8833123.html
Copyright © 2011-2022 走看看