1. 程式人生 > >【NOI2017】數字格

【NOI2017】數字格

Description

菁菁堂有一塊數字格,那是王解體最喜歡去的地方。
傳說中,這條氣勢磅礴的數字格,有N行N列,每一個格子裡均有一個數。
敢於挑戰自己的王解體決定來挑戰這道通過率為百分之九十九的題目。
格子的第一行及第一列均是給定的:
F[k,1]=lk
F[1,k]=tk
對於其他格子,滿足遞推式:
F[i,j]=a*F[i,j-1]+b*F[i-1,j]+c
不出所料,當王解體能得到 F[n,n](mod 1000003)時,通過率將達到百分之百。

Solution

這道題可以用FFT打,但是我不會(……),所以用了另外一種巧妙的方法。
首先通過比較暴力的公式推導可以發現,特殊項的貢獻是可以直接算出來的,而常數項c的解決會有一些麻煩,我們先把不含c的算出來,在推導c的貢獻時,可以從係數入手,最左上角的c的係數肯定是1,向右拓展時是a的冪,向下拓展時是b的冪。由於每一個格子的數受它的上面和左邊的數影響,若是斜向來看,每一斜列的係數和是上一列的(a+b)倍。(詳見圖,a=3,b=5時)
這裡寫圖片描述


黃色塊的和為:1(3+5),灰色塊的和為:1(3+5)2,藍色塊的和為:1(3+5)3
但是當超過對角線時,就會有兩塊多餘的被計入係數和中(圖中的綠塊為多餘塊)。
這裡寫圖片描述
怎麼減去呢?我們可以發現實際上就等於灰色塊對下一斜列的貢獻,灰色塊的計算是很容易的,這裡就不推導了,用組合數再乘上a的冪和b的冪就可以了。最後c的貢獻就是矩陣的係數和乘上c。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std; #define fo(i,a,b) for(i=a;i<=b;i++) typedef long long ll; const int N=2e5+5,mo=1e6+3; int n,a,b,c,i,j; ll x[N],y[N],ans,Cn,Cm,C,jc[N*2],ni[N*2],ca,cb,t,tot,num; ll ksm(int x,int y){ if(y==0) return 1; if(y%2) return (ksm(x,y-1)*x)%mo; t=ksm(x,y/2);t=t*t%mo; return
t; } ll sgm(ll x,ll y){ return jc[y]*ni[x]%mo*ni[y-x]%mo; } int main(){ freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); jc[0]=ni[0]=1; fo(i,1,2*N) jc[i]=(jc[i-1]*i)%mo,ni[i]=ksm(jc[i],mo-2); while(scanf("%d%d%d%d",&n,&a,&b,&c)!=EOF){ ans=0; fo(i,1,n) scanf("%lld",&y[i]); fo(i,1,n) scanf("%lld",&x[i]); Cn=0;Cm=n-2;ca=ksm(a,n-1);cb=ksm(b,n-1); fo(i,1,n-1){ C=sgm(Cn,Cm); Cn++,Cm++; ans=(ans+(C*ca%mo)*y[n-i+1])%mo; ans=(ans+(C*cb%mo)*x[n-i+1])%mo; ca=ca*b%mo,cb=cb*a%mo; } n--; tot=0;num=1; fo(i,1,n) tot=(tot+num)%mo,num=num*(a+b)%mo; ca=ksm(a,n),num-=ca; cb=ksm(b,n),num-=cb; tot=(tot+num)%mo,num=num*(a+b)%mo; fo(i,1,n-1){ C=sgm(i,n+i-1); ca=C*ksm(a,n-1)%mo*ksm(b,i)%mo*a%mo; num-=ca;if(num<0) num+=mo; cb=C*ksm(a,i)%mo*ksm(b,n-1)%mo*b%mo; num-=cb;if(num<0) num+=mo; tot=(tot+num)%mo,num=num*(a+b)%mo; } ans=(ans+tot*c)%mo; while(ans<0) ans+=mo; printf("%lld\n",ans); } }