zoukankan      html  css  js  c++  java
  • CF623E Transforming Sequence

    Transforming Sequence

    给定(n,k)

    让你构造序列(a(0<a_i<2^k)),满足(b_i(b_i=a_1 extrm{or} a_2 extrm{or} cdots extrm{or} a_i))严格单调递增。(or为按位或)

    问你方案总数。对(10^9+7)取模。

    (nleq 10^{18},kleq 30000)

    zhouzhendong的题解

    考虑有(k)个数位,要保证单调递增,由于是or运算,所以值为(1)的数位不管怎样或都不变了,所以每多一个(a_i)至少要多让一个数位变成(1)

    因为只有(k)个数位,所以(a)的元素个数最多有(n)个,即(nleq k)。当(n>k)时就是无解,输出0。

    大力DP

    (dp_{i,j})表示前(i)个数占用了(j)个二进制位的方案数。注意这里不考虑占用的是哪几个二进制位,在最后乘以一个(inom{k}{n})就行了。

    则不难列出转移方程:

    [dp_{i+1,x}=sum_{j=0}^{x-1}2^jinom{x}{j}dp_{i,j} ]

    注意(j)(0)开始枚举其实和从(i)开始是等价的。

    因为原本就是(1)的数位,新增的数的那一位不管是(0)还是(1)都等价,所以要乘(2^j)。组合数意义很明显。

    但是这样太慢了,要TLE。

    DP优化

    我们发现这个DP可以成段的转移。

    设原本有(x)个数,现在一下子加入(y)个数,写出转移方程:

    [dp_{x+y,i}=sum_{j=0}^{i}2^{jy}inom{i}{j}dp_{x,j}cdot dp_{y,i-j} ]

    其中,由于要加入(y)个数字,每一个数字都在原有的(j)个二进制位(1)中贡献了(2^j)的系数(和之前同理),所以总的系数贡献就是((2^j)^y=2^{jy})了。

    我们发现这个是个多项式卷积的形式,所以可以直接FFT优化。注意这个FFT要拆系数,不然要爆long long

    这样的话我们可以运用倍增的套路来解决整个运算过程。

    最终的答案就是(sum_{i=0}^{k}inom{k}{i}dp_{n,i})

    注意,我代码里面把(k)写成了(m)。时间复杂度(O(klog^2 k))


    练习了一下拆系数,没有想象中的那么难,简单来说就是拆一个(2^{15})出来。总共要做7次FFT,感觉三模数就是个废物。

    co double pi=acos(-1);
    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};
    }
    
    co int mod=1e9+7;
    il int add(int a,int b){
    	return (a+=b)>=mod?a-mod:a;
    }
    il int mul(int a,int b){
    	return (LL)a*b%mod;
    }
    il int fpow(int a,int b){
    	int ans=1;
    	for(;b;b>>=1,a=mul(a,a))
    		if(b&1) ans=mul(ans,a);
    	return ans;
    }
    
    co int N=1<<17;
    int n,m;
    int fac[N],ifac[N];
    node w[N],a1[N],a2[N],b1[N],b2[N],A[N],B[N],C[N];
    int lim,len,rev[N];
    
    void trans(node a[]){
    	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){
    		int quot=lim/(step<<1);
    		for(int i=0;i<lim;i+=step<<1){
    			int j=i+step;
    			for(int k=0;k<step;++k){
    				node t=w[quot*k]*a[j+k];
    				a[j+k]=a[i+k]-t,a[i+k]=a[i+k]+t;
    			}
    		}
    	}
    }
    void mul_to(int a[],int b[],int c[]){
    	for(int i=0;i<lim;++i){
    		a1[i]=(node){a[i]>>15,0},a2[i]=(node){a[i]&0x7fff,0};
    		b1[i]=(node){b[i]>>15,0},b2[i]=(node){b[i]&0x7fff,0};
    	}
    	trans(a1),trans(a2),trans(b1),trans(b2);
    	for(int i=0;i<lim;++i){
    		A[i]=a1[i]*b1[i];
    		B[i]=a1[i]*b2[i]+a2[i]*b1[i];
    		C[i]=a2[i]*b2[i];
    		w[i].y=-w[i].y;
    	}
    	trans(A),trans(B),trans(C);
    	for(int i=0;i<lim;++i){
    		A[i].x=round(A[i].x/lim);
    		B[i].x=round(B[i].x/lim);
    		C[i].x=round(C[i].x/lim);
    		c[i]=add(mul((LL)A[i].x%mod,(1LL<<30)%mod),add(mul((LL)B[i].x%mod,(1LL<<15)%mod),(LL)C[i].x%mod));
    		w[i].y=-w[i].y;
    	}
    }
    
    int f[N],g[N],D[N],E[N];
    
    void merge(int a[],int b[],int c[],int&x,int&y){
    	for(int i=0;i<lim;++i) D[i]=E[i]=0;
    	for(int i=0;i<=m;++i){
    		D[i]=mul(a[i],mul(ifac[i],fpow(2,(LL)y*i%(mod-1))));
    		E[i]=mul(b[i],ifac[i]);
    	}
    	mul_to(D,E,c);
    	for(int i=0;i<=m;++i) c[i]=mul(c[i],fac[i]);
    	for(int i=m+1;i<lim;++i) c[i]=0;
    	x+=y;
    }
    
    int main(){
    	LL nn=read<LL>();read(m);
    	if(nn>m) return puts("0"),0;
    	else n=nn;
    	fac[0]=1;
    	for(int i=1;i<=m;++i) fac[i]=mul(fac[i-1],i);
    	ifac[m]=fpow(fac[m],mod-2);
    	for(int i=m-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
    	len=ceil(log2((m<<1)+1)),lim=1<<len;
    	for(int i=0;i<lim;++i){
    		rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
    		w[i]=(node){cos(i*2*pi/lim),sin(i*2*pi/lim)};
    	}
    	f[0]=1;
    	for(int i=1;i<=m;++i) g[i]=1; // g[0]=1 -> epsilon
    	for(int x=0,y=1;y<=n;merge(g,g,g,y,y))
    		if(n&y) merge(f,g,f,x,y);
    	int ans=0;
    	for(int i=0;i<=m;++i)
    		ans=add(ans,mul(f[i],mul(fac[m],mul(ifac[i],ifac[m-i]))));
    	printf("%d
    ",ans);
    	return 0;
    }
    

    这个Warning

    narrowing conversion of '……' from 'int' to 'double' inside { } is ill-formed in C++11 [-Wnarrowing]

    神烦,C++11跟我有什么关系。

  • 相关阅读:
    js隐藏嵌入表边框
    把字符串中的小写字母转换成大写字母
    字符串逆序
    嵌入式C语言编程与AVR技巧(一)——C语言环境访问MCU寄存器
    寻找第K大的数的方法总结
    ASCII码(全)
    把字符串中的小写字母转换成大写字母
    纯C 字符串操作函数 实现 (strcpy, strncpy, memcpy, memset, strcat, strlen ... ) .
    ASCII码(全)
    字符串逆序
  • 原文地址:https://www.cnblogs.com/autoint/p/11272637.html
Copyright © 2011-2022 走看看