【codeforces 623E】dp+FFT+快速幂

题目大意:用$[1,2^k-1]$之间的证书构造一个长度为$n$的序列$a_i$,令$b_i=a_1\ or\ a_2\ or\ ...\ or a_i$,问使得b序列严格递增的方案数,答案对$10^9+7$取模。

数据范围,$n≤1^{18}$,$k≤30000$。

考虑用dp来解决这一题,我们用$f[i][j]$来表示前$i$个数中,使用了$j$个二进制位(注意!并不是前$j$个),那么答案显然为$\sum_{i=0}^{k} \binom{n}{i} \times f[n][i]$。

考虑如何用前面求得的数值来更新$f[x+y][i]$,不妨设$j∈[1,i]$。

不难推出,用了$x$个数,在$i$个二进制位中选用了$j$个二进制位的方案数为$\binom{i}{j} \times f[x][j]$。

然后,用掉$y$个数,并选用余下$i-j$个二进制位的方案数为$f[y][i-j]$。

考虑到前面$x$个数已经选择了$j$个二进制位,那么剩下的$y$个数,在这$j$个位置上,均可以随便填0或1,方案数为$(2^j)^y$。

通过上文分析,得$f[x+y][i]=\sum_{j=1}^{i} f[x][j] \times \binom{i}{j} \times f[y][i-j] \times (2^j)^y$。

通过简单整理,$=i!\sum_{j=1}^{i} \frac{f[x][j]\times (2^j)^y}{j!} \times \frac{f[y][i-j]}{(i-j)!}$。

然后,我们就可以通过NTT,进行dp式子的转移。

不过此题的模数非常恶心,所以需要用任意模数FFT。

考虑到$n$范围非常大,所以$x$和$y$的选择必须要有技巧,我们可以用类似快速幂的算法,加速转移,详情可见代码。

时间复杂度为$O(k\ log\ k\ log\ n)$。

  1 #include<bits/stdc++.h>
  2 #define L long long
  3 #define MOD 1000000007
  4 #define H 16
  5 #define M 1<<H
  6 #define hh 32768
  7 #define PI acos(-1)
  8 using namespace std;
  9 int nn;
 10 int k; L n;
 11
 12 L pow_mod(L x,L k){
 13     L ans=1;
 14     while(k){
 15         if(k&1) ans=ans*x%MOD;
 16         k>>=1; x=x*x%MOD;
 17     }
 18     return ans;
 19 }
 20
 21 struct cp{
 22     double i,r;
 23     cp(){i=r=0;}
 24     cp(double rr,double ii){i=ii; r=rr;}
 25     friend cp operator +(cp a,cp b){return cp(a.r+b.r,a.i+b.i);}
 26     friend cp operator -(cp a,cp b){return cp(a.r-b.r,a.i-b.i);}
 27     friend cp operator *(cp a,cp b){return cp(a.r*b.r-a.i*b.i,a.r*b.i+a.i*b.r);}
 28     L D(){L hhh=(r+0.499); return hhh%MOD;}
 29 };
 30
 31 cp w[M][H];
 32 void init(){
 33     for(int i=2,j=0;j<H;j++,i<<=1){
 34         for(int k=0;k<i;k++)
 35         w[k][j]=cp(cos(2*PI*k/i),sin(2*PI*k/i));
 36     }
 37 }
 38
 39 void change(cp a[],int n){
 40     for(int i=0,j=0;i<n-1;i++){
 41         if(i<j) swap(a[i],a[j]);
 42         int k=n>>1;
 43         while(j>=k) j-=k,k>>=1;
 44         j+=k;
 45     }
 46 }
 47 void FFT(cp a[],int n,int on){
 48     change(a,n);
 49     cp wn,u,t;
 50     for(int h=2,i=0;h<=n;h<<=1,i++){
 51         for(int j=0;j<n;j+=h){
 52             for(int k=j;k<j+(h>>1);k++){
 53                 wn=w[k-j][i]; if(on==-1) wn.i=-wn.i;
 54                 u=a[k]; t=a[k+(h>>1)]*wn;
 55                 a[k]=u+t; a[k+(h>>1)]=u-t;
 56             }
 57         }
 58     }
 59     if(on==-1)
 60         for(int i=0;i<n;i++) a[i].r=a[i].r/n;
 61 }
 62
 63 struct poly{
 64     L a[M];
 65     poly(){memset(a,0,sizeof(a));}
 66     friend poly operator *(poly a,poly b){
 67         poly c;
 68         static cp A[M],B[M],C[M],D[M],E[M],F[M],G[M];
 69         memset(A,0,sizeof(A)); memset(B,0,sizeof(B));
 70         memset(C,0,sizeof(C)); memset(D,0,sizeof(D));
 71         for(int i=0;i<nn;i++) A[i].r=a.a[i]%hh,B[i].r=a.a[i]/hh;
 72         for(int i=0;i<nn;i++) C[i].r=b.a[i]%hh,D[i].r=b.a[i]/hh;
 73         FFT(A,nn,1); FFT(B,nn,1); FFT(C,nn,1); FFT(D,nn,1);
 74         for(int i=0;i<nn;i++){
 75             E[i]=A[i]*C[i];
 76             F[i]=A[i]*D[i]+B[i]*C[i];
 77             G[i]=B[i]*D[i];
 78         }
 79         FFT(E,nn,-1); FFT(F,nn,-1); FFT(G,nn,-1);
 80         for(int i=0;i<nn;i++)
 81             c.a[i]=(E[i].D()+F[i].D()*hh%MOD+G[i].D()*hh%MOD*hh%MOD)%MOD;
 82         for(int i=k+1;i<nn;i++) c.a[i]=0;
 83         return c;
 84     }
 85 };
 86 L fac[M]={0},invfac[M]={0};
 87 L C(int n,int m){return fac[n]*invfac[m]%MOD*invfac[n-m]%MOD;}
 88 poly ans,f,f1,f2;
 89 int main(){
 90     init();
 91     cin>>n>>k; n--;
 92     fac[0]=1; for(int i=1;i<=k;i++) fac[i]=fac[i-1]*i%MOD;
 93     invfac[k]=pow_mod(fac[k],MOD-2);
 94     for(int i=k-1;~i;i--) invfac[i]=invfac[i+1]*(i+1)%MOD;
 95     for(nn=1;nn<=(k*2);nn<<=1);
 96     L now=1;
 97     for(int i=1;i<=k;i++) f.a[i]=1;
 98     ans=f;
 99     while(n){
100         if(n&1){
101             f1=ans; f2=f;
102             for(int i=1;i<=k;i++)
103             f1.a[i]=f1.a[i]*invfac[i]%MOD*pow_mod(pow_mod(2,i),now)%MOD;
104             for(int i=1;i<=k;i++)
105             f2.a[i]=f2.a[i]*invfac[i]%MOD;
106             ans=f1*f2;
107             for(int i=1;i<=k;i++)
108             ans.a[i]=ans.a[i]*fac[i]%MOD;
109         }
110         f1=f; f2=f;
111         for(int i=1;i<=k;i++)
112         f1.a[i]=f1.a[i]*invfac[i]%MOD*pow_mod(pow_mod(2,i),now)%MOD;
113         for(int i=1;i<=k;i++)
114         f2.a[i]=f2.a[i]*invfac[i]%MOD;
115         f=f1*f2;
116         for(int i=1;i<=k;i++)
117         f.a[i]=f.a[i]*fac[i]%MOD;
118         n>>=1; now<<=1;
119     }
120     L sum=0;
121     for(int i=1;i<=k;i++)
122     sum=(sum+ans.a[i]*C(k,i))%MOD;
123     cout<<sum<<endl;
124 }

原文地址:https://www.cnblogs.com/xiefengze1/p/9094627.html

时间: 2024-08-29 19:13:36

【codeforces 623E】dp+FFT+快速幂的相关文章

POJ3735 Training little cats DP,矩阵快速幂,稀疏矩阵优化

题目大意是,n只猫,有k个动作让它们去完成,并且重复m次,动作主要有三类gi,ei,s i j,分别代表第i只猫获得一个花生,第i只猫吃掉它自己所有的花生,第i只和第j只猫交换彼此的花生.k,n不超过100,m不超过1000,000,000,计算出最后每只猫还剩下多少个花生. 我们假设一个n维向量P,每个分量的值代表这n只猫所拥有的花生数,那么对于gi操作其实就是在第i维分量上加上1:对于ei,那就是在第i维分量上乘以0,说到这里,有木有感觉这很像3D坐标转化中的平移矩阵和缩放矩阵?没错,就是这

HDU 2294 Pendant (DP+矩阵快速幂降维)

HDU 2294 Pendant (DP+矩阵快速幂降维) ACM 题目地址:HDU 2294 Pendant 题意: 土豪给妹子做首饰,他有K种珍珠,每种N个,为了炫富,他每种珍珠都要用上.问他能做几种长度[1,N]的首饰. 分析: 1 ≤ N ≤ 1,000,000,000简直可怕. 首先想dp,很明显可以想到: dp[i][j] = (k-(j-1))*dp[i-1][j-1] + j*dp[i-1][j](dp[i][j]表示长度为i的并且有j种珍珠的垂饰有多少个) 然后遇到N太大的话,

POJ3420 Quad Tiling DP + 矩阵快速幂

题目大意是用1*2的骨牌堆积成4*N的矩形,一共有多少种方法,N不超过10^9. 这题和曾经在庞果网上做过的一道木块砌墙几乎一样.因为骨牌我们可以横着放,竖着放,我们假设以4为列,N为行这样去看,并且在骨牌覆盖的位置上置1,所以一共最多有16种状态.我们在第M行放骨牌的时候,第M+1行的状态也是有可能被改变的,设S(i,j)表示某一行状态为i时,将其铺满后下一行状态为j的方案书.考虑下如果我们让矩阵S和S相乘会有什么意义,考虑一下会发现S*S的意义当某行状态为i,接着其后面第2行的状态为j的可行

HDU5863 cjj&#39;s string game(DP + 矩阵快速幂)

题目 Source http://acm.split.hdu.edu.cn/showproblem.php?pid=5863 Description cjj has k kinds of characters the number of which are infinite. He wants to build two strings with the characters. The lengths of the strings are both equal to n. cjj also def

HDU 5434 Peace small elephant 状压dp+矩阵快速幂

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5434 Peace small elephant Accepts: 38 Submissions: 108 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) 问题描述 小明很喜欢国际象棋,尤其喜欢国际象棋里面的大象(只要无阻挡能够斜着走任意格),但是他觉得国际象棋里的大象太凶残了,于是他

hdu 4878 ZCC loves words(AC自动机+dp+矩阵快速幂+中国剩余定理)

hdu 4878 ZCC loves words(AC自动机+dp+矩阵快速幂+中国剩余定理) 题意:给出若干个模式串,总长度不超过40,对于某一个字符串,它有一个价值,对于这个价值的计算方法是这样的,设初始价值为V=1,假如这个串能匹配第k个模式串,则V=V*prime[k]*(i+len[k]),其中prime[k]表示第k个素数,i表示匹配的结束位置,len[k]表示第k个模式串的长度(注意,一个字符串可以多次匹配同意个模式串).问字符集为'A'-'Z'的字符,组成的所有的长为L的字符串,

UVA 11651 - Krypton Number System(DP+矩阵快速幂)

UVA 11651 - Krypton Number System 题目链接 题意:给一个进制base,一个分数score求该进制下,有多少数满足一下条件: 1.没有连续数字 2.没有前导零 3.分数为score,分数的计算方式为相邻数字的平方差的和 思路:先从dp入手,dp[i][j]表示组成i,最后一个数字为j的种数,然后进行状态转移,推出前面一步能构成的状态,也就是到dp[(b - 1) * (b - 1)][x]. 然后可以发现后面的状态,都可以由前面这些状态统一转移出来,这样就可以利用

Codeforces Round #291 (Div. 2) E - Darth Vader and Tree (DP+矩阵快速幂)

这题想了好长时间,果断没思路..于是搜了一下题解.一看题解上的"快速幂"这俩字,不对..这仨字..犹如醍醐灌顶啊...因为x的范围是10^9,所以当时想的时候果断把dp递推这一方法抛弃了.我怎么就没想到矩阵快速幂呢.......还是太弱了..sad..100*100*100*log(10^9)的复杂度刚刚好. 于是,想到了矩阵快速幂后,一切就变得简单了.就可以把距离<=x的所有距离的点数都通过DP推出来,然后一个快速幂就解决了. 首先DP递推式很容易想到.递推代码如下: for(

HDU 4599 Dice (概率DP+数学+快速幂)

题意:给定三个表达式,问你求出最小的m1,m2,满足G(m1) >= F(n), G(m2) >= G(n). 析:这个题是一个概率DP,但是并没有那么简单,运算过程很麻烦. 先分析F(n),这个用DP来推公式,d[i],表示抛 i 次连续的点数还要抛多少次才能完成.那么状态转移方程就是 d[i] = 1/6*(1+d[i+1]) + 5/6*(1+d[1]), 意思就是说在第 i 次抛和上次相同的概率是1/6,然后加上上次抛的和这一次,再加上和上次不同的,并且又得从第1次开始计算. 边界就是