BZOJ4451 [Cerc2015]Frightful Formula 多项式 FFT 递推 组合数学
原文链接http://www.cnblogs.com/zhouzhendong/p/8820963.html
题目传送门 - BZOJ4451
题意
给你一个$n\times n$矩阵的第一行和第一列,其余的数通过如下公式推出:
$$f_{i,j}=a\cdot f_{i,j-1}+b\cdot f_{i-1,j}+c$$
题解
利用$FFT$来解决此题
真是一道好题只是我太菜了。光$FFT$就调了好久。当然本题可以直接递推(将写在用$FFT$实现的算法的后面),用$FFT$只是用来给脑子偷懒的。
这题的主要思路很巧妙。
先假设$a=b=1$。
我们考虑对于每一个$f_{i,j}$计算它对$f_{n,n}$的贡献次数。
显然贡献次数就是在网格图中从$(i,j)$开始只能往右或者往下走的路径条数,即$\binom{n-i+n-j}{n-i}$。
注意第一行和第一列的对答案的贡献式有点不同。
因为第一行的格子不能对右边的格子做贡献,所以:
第一行的格子$(1,i)$的贡献次数就是$\binom{2n-i-2}{n-i}$。
同理,
第一列的格子$(i,1)$的贡献次数就是$\binom{2n-i-2}{n-i}$。
现在不考虑$a=b=1$。
我们还要乘一个权值。考虑从第$i$行走到第$n$行向下走了$n-i$次,所以乘了$n-i$次$b$,即乘了$b^{n-i}$。同理向右走要乘的权值就是$a^{n-j}$。所以综上所述,我们再重新写一下贡献次数的式子。
下面的三式满足$i,j>1$。注意$(1,1)$是没有贡献次数的。(一开始没注意到,被坑了很久)
$(1,i):\binom{2n-i-2}{n-i}a^{n-1}b^{n-i}$
$(i,1):\binom{2n-i-2}{n-i}a^{n-i}b^{n-1}$
$(i,j):\binom{2n-i-j}{n-i}a^{n-i}b^{n-j}$
考虑到$(i,j)$这种格子的值里面含有$(i,1)$和$(1,i)$这种的贡献,贡献不能重复计算,所以我们在计算格子$(i,j)$的时候就只考虑它在被贡献的时候产生的$c$对答案的贡献次数。
(就是上面的那个)
所以,我们可以列出答案的式子:
$$ans=\sum_{i=2}^{n}f_{i,1}\cdot\binom{2n-i-2}{n-i}a^{n-i}b^{n-1}\\+\sum_{i=2}^{n}f_{1,i}\cdot\binom{2n-i-2}{n-i}a^{n-i}b^{n-1}\\+\sum_{i=2}^{n}\sum_{j=2}^{n}c\cdot\binom{2n-i-j}{n-i}a^{n-i}b^{n-j}$$
各种逆元、阶乘以及幂的预处理就不说了。
于是前面的两个式子都可以$O(n)$搞定。
后面的那个式子,我们在给他稍稍变几个形:
$$\sum_{i=2}^{n}\sum_{j=2}^{n}c\cdot\binom{2n-i-j}{n-i}a^{n-i}b^{n-j}$$
把$c$提前,并修改求和指标,枚举$n-i$和$n-j$,得:
$$=c\sum_{i=0}^{n-2}\sum_{j=0}^{n-2}\frac{(i+j)!}{i!j!}a^ib^j$$
$$=c\sum_{i=0}^{n-2}i!\sum_{j=0}^{n-2}\frac{a^{(i-j)}}{(i-j)!}\cdot\frac{b^j}{j!}$$
设
$$f_i=\begin{cases}\large{\frac{a^i}{i!}}& \text{$(0\leq i\leq n-2)$}\\0& \text{$(n-2<i)$}\end{cases}$$
$$g_i=\begin{cases}\large{\frac{b^i}{i!}}& \text{$(0\leq i\leq n-2)$}\\0& \text{$(n-2<i)$}\end{cases}$$
则原式可以写为:
$$=c\sum_{i=0}^{n-2}(i+j)!\sum_{j=0}^{n-2}f_ig_j$$
发现这个符合多项式卷积形式。于是我们再换一个写法:
$$=c\sum_{i=0}^{2n-4}i!\sum_{j=0}^{i}f_jg_{i-j}$$
于是显然可以$FFT$快速计算了。注意,要取模,而且卷积得到的数字较大,可以用任意模数$FFT$或者拆系数$FFT$。我写的是拆系数。因为这样好写。
时间复杂度$O(n\log n)$。
但是这样不仅跑的慢,写起来也麻烦。
这题其实可以把$n$搞到$3000000$级别,把模数搞到$1e9+7$。
因为存在线性递推方式。
线性递推
根本没想到还有这样的操作。可能是本着练习$FFT$的目的然后就没仔细想了。
可以线性递推。
我们把之前的式子继续推下去。
$$c\sum_{i=0}^{n-2}\sum_{j=0}^{n-2}\frac{(i+j)!}{i!j!}a^ib^j$$
我们只需要考虑后面的式子,先不看$c$。
$$\sum_{i=0}^{n-2}\sum_{j=0}^{n-2}\binom{i+j}{i}a^ib^j$$
设
$$f_n=\sum_{i=0}^{n}\sum_{j=0}^{n}\binom{i+j}{i}a^ib^j$$
则我们要求的就是$f_{n-2}$。
推导:
$$\begin{eqnarray*}f_n&=&\sum_{i=0}^{n}\sum_{j=0}^{n}\binom{i+j}{i}a^ib^j\\&=&\sum_{i=0}^{n}a^i\sum_{j=0}^{n}\binom{i+j}{i}b^j\\&=&\sum_{i=0}^{n-1}a^i\sum_{j=0}^{n-1}\binom{i+j}{i}b^j+a^n\sum_{j=0}^{n}\binom{n+j}{n}b^j+b^n\sum_{i=0}^{n}a^i\binom{i+n}{i}-\binom{2n}{n}a^nb^n\\&=&f_{n-1}+a^n\sum_{i=0}^{n}\binom{n+i}{n}b^i+b^n\sum_{i=0}^{n}\binom{n+i}{n}a^i-\binom{2n}{n}a^nb^n\end{eqnarray*}$$
我们只需要处理$\sum_{i=0}^{n}\binom{n+i}{n}b^i$和$\sum_{i=0}^{n}\binom{n+i}{n}a^i$,就可以搞定了。
由于这两个形式一样,我这里只对$a$进行推导。
设
$$ga_n=\sum_{i=0}^{n}\binom{n+i}{n}a^i$$
则
$$f_n=f_{n-1}+a^ngb_n+b^nga_n-\binom{2n}{n}a^nb^n$$
推一下 $ga_n$,
$$ga_n = \sum_{i=0}^{n}\left(\binom{n-1+i}{i}+\binom{n-i+1}{i-1}\right)a^i\\=ga_{n-1} +\binom{2n-1}{n}a^n+\sum_{i=0}^{n-1} \binom{n+i}{i}a^{i+1}\\=ga_{n-1}+\binom{2n-1}{n}a^{n}+a\cdot ga_n-\binom{2n}{n}a^{n+1}$$
移项,并两边同时除以$1-a$:
$$\begin{eqnarray*}&ga_n&=&ga_{n-1}+\binom{2n-1}{n}a^n+a\cdot ga_n-\binom{2n}{n}a^{n+1}\\ \Longrightarrow& (1-a)ga_n&=&ga_{n-1}+\binom{2n-1}{n}a^n-\binom{2n}{n}a^{n+1}\\ \Longrightarrow& ga_n&=&\frac{ga_{n-1}+\binom{2n-1}{n}a^n-\binom{2n}{n}a^{n+1}}{1-a}\end{eqnarray*}$$
然后就搞定了$ga$的递推,$gb$同理。
但是有特殊情况。
当$a=1$时,$1-a=0$,分母为$0$,这个公式挂掉了。
于是我们要解决这个问题。
方法是直接把$a=1$代入移项后的式子里面:
$$\begin{eqnarray*}&(1-a)ga_n&=&ga_{n-1}+\binom{2n-1}{n}a^n-\binom{2n}{n}a^{n+1}\\ \Longrightarrow& ga_{n-1}&=&\binom{2n}{n}-\binom{2n-1}{n}=\binom{2n-1}{n-1}\\ \Longrightarrow& ga_{n}&=&\binom{2n+1}{n}\end{eqnarray*}$$
就可以直接算$ga_n$了。
于是,
$$f_n=f_{n-1}+a^ngb_n+b^nga_n-\binom{2n}{n}a^nb^n$$
综合上面的两种递推式,写三支递推就可以搞定本题了。
而且一不留神卡常到第一了QAQ。
No. | RunID | User | Memory | Time | Language | Code_Length | Submit_Time |
1 | 2705999 | zhouzhendong | 24728 KB | 580 MS | C++ | 1649 B | 2018-04-13 19:12:05 |
代码
代码1 - FFT实现
- #include <bits/stdc++.h>
- using namespace std;
- typedef long long LL;
- const int N=1<<19;
- double PI=acos(-1.0);
- LL mod=1000003;
- LL n,a,b,c,ans=0;
- LL Fac[N],Inv[N],Powa[N],Powb[N];
- LL fa[N],fb[N],tot[N];
- int s,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);}
- }w[N],A[N],B[N];
- LL Pow(LL x,LL y){
- if (y==0)
- return 1LL;
- LL xx=Pow(x,y/2);
- xx=xx*xx%mod;
- if (y&1LL)
- xx=xx*x%mod;
- return xx;
- }
- LL inv(LL x){
- return Pow(x,mod-2);
- }
- LL func_C(int n,int m){
- return Fac[n]*Inv[m]%mod*Inv[n-m]%mod;
- }
- void FFT(C a[],int n){
- 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_mul(C A[],C B[]){
- FFT(A,s),FFT(B,s);
- for (int i=0;i<s;i++)
- A[i]=A[i]*B[i],w[i].i*=-1.0;
- FFT(A,s);
- }
- int main(){
- scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
- Fac[0]=Powa[0]=Powb[0]=1;
- for (int i=1;i<=n*2;i++){
- Fac[i]=Fac[i-1]*i%mod;
- Powa[i]=Powa[i-1]*a%mod;
- Powb[i]=Powb[i-1]*b%mod;
- }
- Inv[n*2]=inv(Fac[n*2]);
- for (int i=n*2-1;i>=0;i--)
- Inv[i]=Inv[i+1]*(i+1)%mod;
- for (int i=1,x;i<=n;i++){
- scanf("%d",&x);
- if (i>1)
- ans=(ans+func_C(n+n-i-2,n-i)*Powa[n-1]%mod*Powb[n-i]%mod*x)%mod;
- }
- for (int i=1,x;i<=n;i++){
- scanf("%d",&x);
- if (i>1)
- ans=(ans+func_C(n+n-i-2,n-i)*Powa[n-i]%mod*Powb[n-1]%mod*x)%mod;
- }
- for (int i=0;i<=n-2;i++)
- fa[i]=Powa[i]*Inv[i]%mod,fb[i]=Powb[i]*Inv[i]%mod;
- for (s=1,L=0;s<=2*n-4;s<<=1,L++);
- for (int i=0;i<s;i++){
- R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
- w[i]=C(cos(2*i*PI/s),sin(2*i*PI/s));
- }
- LL ansdc=0;
- for (int i=0;i<s;i++)
- A[i]=C(fa[i]&1023,0),B[i]=C(fb[i]&1023,0);
- FFT_mul(A,B);
- for (int i=0;i<s;i++){
- LL v=((LL)(A[i].r/s+0.5))%mod;
- ansdc=(ansdc+Fac[i]*v)%mod;
- tot[i]=v,w[i].i*=-1.0;
- }
- for (int i=0;i<s;i++)
- A[i]=C(fa[i]>>10,0),B[i]=C(fb[i]>>10,0);
- FFT_mul(A,B);
- for (int i=0;i<s;i++){
- LL v=((LL)(A[i].r/s+0.5))%mod;
- ansdc=(ansdc+Fac[i]*48573%mod*v)%mod;
- tot[i]=(tot[i]+v)%mod,w[i].i*=-1.0;
- }
- for (int i=0;i<s;i++){
- A[i]=C((fa[i]>>10)+(fa[i]&1023),0);
- B[i]=C((fb[i]>>10)+(fb[i]&1023),0);
- }
- FFT_mul(A,B);
- for (int i=0;i<s;i++){
- LL v=((LL)(A[i].r/s+0.5))%mod;
- ansdc=(ansdc+Fac[i]*1024%mod*(v-tot[i]+mod))%mod;
- w[i].i*=-1.0;
- }
- ans=(ans+ansdc*c)%mod;
- printf("%lld",ans);
- return 0;
- }
代码2 - 递推(没卡常)(代码3卡了一点常数的(跑的挺快))
- #include <bits/stdc++.h>
- using namespace std;
- typedef long long LL;
- const int N=200005,mod=1000003;
- 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;
- }
- LL n,a,b,c,ans=0;
- LL Fac[mod],Inv[mod],Powa[N],Powb[N];
- LL f[N],ga[N],gb[N];
- LL Pow(LL x,LL y){
- if (y==0)
- return 1LL;
- LL xx=Pow(x,y/2);
- xx=xx*xx%mod;
- if (y&1LL)
- xx=xx*x%mod;
- return xx;
- }
- LL inv(int x){
- return Pow((x%mod+mod)%mod,mod-2);
- }
- LL C(int n,int m){
- return Fac[n]*Inv[m]%mod*Inv[n-m]%mod;
- }
- int main(){
- scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
- Fac[0]=Powa[0]=Powb[0]=1;
- for (int i=1;i<=n;i++){
- Powa[i]=Powa[i-1]*a%mod;
- Powb[i]=Powb[i-1]*b%mod;
- }
- for (int i=1;i<=2*n;i++)
- Fac[i]=Fac[i-1]*i%mod;
- Inv[2*n]=Pow(Fac[2*n],mod-2);
- for (int i=2*n-1;i>=0;i--)
- Inv[i]=Inv[i+1]*(i+1)%mod;
- for (int x=read(),i=2;i<=n;i++)
- ans=(ans+C(n+n-i-2,n-i)*Powa[n-1]%mod*Powb[n-i]%mod*read())%mod;
- for (int x=read(),i=2;i<=n;i++)
- ans=(ans+C(n+n-i-2,n-i)*Powa[n-i]%mod*Powb[n-1]%mod*read())%mod;
- f[0]=ga[0]=gb[0]=1;
- LL inva=inv(1-a),invb=inv(1-b);
- for (int i=1;i<=n-2;i++){
- ga[i]=a==1?C(2*i+1,i):((ga[i-1]+C(2*i-1,i)*Powa[i]-C(2*i,i)*Powa[i+1])%mod*inva%mod);
- gb[i]=b==1?C(2*i+1,i):((gb[i-1]+C(2*i-1,i)*Powb[i]-C(2*i,i)*Powb[i+1])%mod*invb%mod);
- f[i]=(f[i-1]+Powa[i]*gb[i]+Powb[i]*ga[i]-C(2*i,i)*Powa[i]%mod*Powb[i])%mod;
- }
- printf("%lld",((ans+f[n-2]*c)%mod+mod)%mod);
- return 0;
- }
代码3 - 卡常(580MS(截至2018-04-13代码运行速度Rk1))
- #include <bits/stdc++.h>
- using namespace std;
- typedef long long LL;
- const int N=200005,mod=1000003;
- 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;
- }
- LL n,a,b,c,ans=0;
- LL Fac[mod],Inv[mod],Powa[N],Powb[N];
- LL f[N],ga[N],gb[N];
- LL Pow(LL x,LL y){
- if (y==0)
- return 1LL;
- LL xx=Pow(x,y/2);
- xx=xx*xx%mod;
- if (y&1LL)
- xx=xx*x%mod;
- return xx;
- }
- LL inv(int x){
- return Pow((x%mod+mod)%mod,mod-2);
- }
- LL C(int n,int m){
- return Fac[n]*Inv[m]%mod*Inv[n-m]%mod;
- }
- int main(){
- scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
- Fac[0]=Powa[0]=Powb[0]=1;
- for (int i=1;i<=n;i++){
- Powa[i]=Powa[i-1]*a%mod;
- Powb[i]=Powb[i-1]*b%mod;
- }
- for (int i=1;i<=2*n;i++)
- Fac[i]=Fac[i-1]*i%mod;
- Inv[2*n]=Pow(Fac[2*n],mod-2);
- for (int i=2*n-1;i>=0;i--)
- Inv[i]=Inv[i+1]*(i+1)%mod;
- for (int x=read(),i=2;i<=n;i++)
- ans=(ans+C(n+n-i-2,n-i)*Powa[n-1]%mod*Powb[n-i]%mod*read())%mod;
- for (int x=read(),i=2;i<=n;i++)
- ans=(ans+C(n+n-i-2,n-i)*Powa[n-i]%mod*Powb[n-1]%mod*read())%mod;
- f[0]=ga[0]=gb[0]=1;
- LL inva=inv(1-a),invb=inv(1-b);
- for (int i=1;i<=n-2;i++){
- ga[i]=a==1?C(2*i+1,i):((ga[i-1]+C(2*i-1,i)*Powa[i]-C(2*i,i)*Powa[i+1])%mod*inva%mod);
- gb[i]=b==1?C(2*i+1,i):((gb[i-1]+C(2*i-1,i)*Powb[i]-C(2*i,i)*Powb[i+1])%mod*invb%mod);
- f[i]=(f[i-1]+Powa[i]*gb[i]+Powb[i]*ga[i]-C(2*i,i)*Powa[i]%mod*Powb[i])%mod;
- }
- printf("%lld",((ans+f[n-2]*c)%mod+mod)%mod);
- return 0;
- }
BZOJ4451 [Cerc2015]Frightful Formula 多项式 FFT 递推 组合数学的更多相关文章
- BZOJ4451 : [Cerc2015]Frightful Formula
$(i,1)$对答案的贡献为$l_iC(2n-i-2,n-i)a^{n-1}b^{n-i}$. $(1,i)$对答案的贡献为$t_iC(2n-i-2,n-i)*a^{n-i}b^{n-1}$. $(i ...
- bzoj 4451 : [Cerc2015]Frightful Formula FFT
4451: [Cerc2015]Frightful Formula Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 177 Solved: 57[Sub ...
- LG4351 [CERC2015]Frightful Formula
Frightful Formula 给你一个\(n\times n\)矩阵的第一行和第一列,其余的数通过如下公式推出: \[f_{i,j}=a\cdot f_{i,j-1}+b\cdot f_{i-1 ...
- UVa 12034 Race (递推+组合数学)
题意:A,B两个人比赛,名次有三种情况(并列第一,AB,BA).输入n,求n个人比赛时最后名次的可能数. 析:本来以为是数学题,排列组合,后来怎么想也不对.原来这是一个递推... 设n个人时答案为f( ...
- 51nod-1670-打怪兽(递推/组合数学)
1670 打怪兽 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 lyk在玩一个叫做“打怪兽”的游戏.游戏的规则是这样的.lyk一开始会有一个初始 ...
- ACM学习历程—BestCoder 2015百度之星资格赛1001 大搬家(递推 && 组合数学)
Problem Description 近期B厂组织了一次大搬家,所有人都要按照指示换到指定的座位上.指示的内容是坐在位置i 上的人要搬到位置j 上.现在B厂有N 个人,一对一到N 个位置上.搬家之后 ...
- ACM学习历程—NPU1045 2015年陕西省程序设计竞赛网络预赛(热身赛)C题 Graph Theory(递推 && 组合数学 && 大数)
Description In graph theory, a matching or independent edge set in a graph G = (V , E) is a set of e ...
- 【XSY2730】Ball 多项式exp 多项式ln 多项式开根 常系数线性递推 DP
题目大意 一行有\(n\)个球,现在将这些球分成\(k\) 组,每组可以有一个球或相邻两个球.一个球只能在至多一个组中(可以不在任何组中).求对于\(1\leq k\leq m\)的所有\(k\)分别 ...
- Shell Necklace (dp递推改cdq分治 + fft)
首先读出题意,然后发现这是一道DP,我们可以获得递推式为 然后就知道,不行啊,时间复杂度为O(n2),然后又可以根据递推式看出这里面可以拆解成多项式乘法,但是即使用了fft,我们还需要做n次多项式乘法 ...
随机推荐
- 【CF1132F】Clear the String(动态规划)
[CF1132F]Clear the String(动态规划) 题面 CF 题解 考虑区间\(dp\). 增量考虑,每次考虑最后一个字符和谁一起删去,然后直接转移就行了. #include<io ...
- 洛谷P2257 YY的GCD 莫比乌斯反演
原题链接 差不多算自己推出来的第一道题QwQ 题目大意 \(T\)组询问,每次问你\(1\leqslant x\leqslant N\),\(1\leqslant y\leqslant M\)中有多少 ...
- utf8mb4的大小写敏感性测试及其修改方法
utf8mb4的大小写敏感性测试及其修改方法 utf8mb4_ unicode_ ci 与 utf8mb4_ general_ ci 如何选择字符除了需要存储,还需要排序或比较大小,涉及到与编码字符集 ...
- 实战Google深度学习框架-C3-TensorFlow入门
第三章:TensorFlow入门 TensorFlow存在计算模型,数据模型和运算模型(本文用TF代表TensorFlow) 3.1 计算模型-计算图 3.1.1 计算图的概念 TensorFlow这 ...
- kubernetes云平台管理实战: pod资源共享(三)
一.共享容器IP地址 1.查看容器进程 [root@k8s-node1 ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS ...
- C#开发Windows服务详细流程
1.Windows服务简单介绍 Windows服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序,主要用于长时间运行的功能或者执行定时任务.一般情况下,用户不能通过用户界面来安装和启 ...
- ArcMap修改粘滞移动容差防止要素在选择时无意拖动移动
粘滞移动容差将设置一个最小像素数,鼠标指针必须在屏幕上移动了此最小距离时,所选要素才会实际发生移动. 设置粘滞移动容差的结果是延迟移动所选要素,直到指针至少移动了这段距离.此方法可用于在使用“编辑”工 ...
- HTML(五)HTML表格
标准表格 <table border="1"> <caption>Monthly savings</caption> <tr> &l ...
- mysql/mariadb主从复制
主从复制简介 MySQL数据库的主从复制方案,是其自带的功能,并且主从复制并不是复制磁盘上的数据库文件,而是通过binlog日志复制到需要同步的从服务器上. MySQL数据库支持单向.双向.链式级联, ...
- cmd 命令添加防火墙端口
windows dos 命令添加防火墙端口. 示例 123 端口: netsh firewall add portopening protocol = UDP port = name = NTPSER ...