學習筆記:楊輝三角形上莫隊(組合數莫隊)(LULU胡策)
與唐林康的決戰在即,麵筋哥需要一件壓場子的終極武器。
麵筋哥手上有 M 個麵筋,能量值分別為 1-M 的整數。現在麵筋哥想要利用這些麵筋制
作他的終極武器:Ex 麵筋棒。Ex 麵筋棒是一種能夠發射強大劍氣的能量武器。它由一些面
筋按次序連線而成。Ex 麵筋棒可能會發射失敗,麵筋哥無法承受失敗的損失。在 SPW 財團
的資助下,經過上百次的實驗,麵筋哥終於發現了麵筋棒成功發射劍氣的規律:
·麵筋哥臂力有限,拿不動太長的 Ex 麵筋棒,所以麵筋棒的長度 L 不能大於 N,當然
它的長度不能為 0。
·每次發動攻擊時,麵筋棒會被觸發 L 次,第 i 次觸發會啟用前 i 個麵筋,進入蓄能狀
態。
·首先劍氣能量會聚集在已經啟用的能量值最小的麵筋上,然後能量會自發地尋找更高
的能量值,如果某一個麵筋 x 的能量值是所有能量值大於當前麵筋的麵筋中(包括未啟用的
麵筋)最小的一個,那麼劍氣能量會轉移到麵筋 x 上,並提高至 s[x]。如果 x 未啟用,則 Ex
麵筋棒會由於能量溢位而發射失敗。
·當發射能量達到當前已啟用的麵筋中的最大值,則第 i 次觸發的能量聚集完成。
·只有當 L 次觸發的能量全部完成聚集,Ex 麵筋棒才能成功發射劍氣。
麵筋哥急切想擊敗唐林康,他想知道,利用他手上的麵筋,能夠製造出多少種不同的可
以成功發射劍氣的 Ex 麵筋棒。兩種麵筋棒不同當且僅當他們的長度不同,或某一個位置上
麵筋能量不同。由於方案數太大,你只需要告訴他答案除以 19260817 的餘數。
由於能量自發地提高違反了物理定律,這導致了時空的崩塌,世界線之間的隔閡被打破。
這時,你眼前突然出現了 Q 個來自各自世界的麵筋哥,每個麵筋哥都想知道方案總數,但
是他們擁有的麵筋不一樣,並且他們的臂力也不一樣。你需要幫助每一個世界的麵筋哥算出
方案總數。
另外面筋哥害怕自己表述不清而導致你理解出錯,所以他悄咪咪地整理了一份更嚴謹的
表述,在早些時候放進了 ftp 裡面。
【輸入格式】
第一行一個正整數 Q,表示世界線的個數
接下來 Q 行,每行兩個整數 M,N,表示該世界線中,麵筋哥擁有 M 個麵筋,能夠拿動長度
至多為 N 的麵筋棒。
【輸出格式】
Q 行,每行一個整數,表示在該世界的條件下,能夠成功發射的 Ex 麵筋棒數量。
【樣例輸入 1】
1
4 4
【樣例輸出 1】
40
【樣例解釋】
將 Ex 麵筋棒看做數列,則下列不同的組成都是能夠成功發射的:
長度為 1:{1},{2},{3},{4}
長度為 2 : {1 2},{1 3},{1 4},{2 1},{2 3},{2 4},{3 1},{3 2},{3 4},{4 1},{4 2},{4 3}
長度為 3: {1 2 3},{1 2 4},{1 3 4},{2 1 3},{2 1 4},{2 3 1},{2 3 4},{2 4 1},{3 1 4},{3 2 1},{3 2 4},{3 4 1},{3
4 2},{4 2 1},{4 3 1},{4 3 2}
LULU隨手組的數學題
有點毒瘤
我們發現由於題目的性質:
所以選出一些數的代價是相同的:為
但是對於一段數它的代價是可以DP的
我們列舉當前最大的值的位置:
如果最大值在:第i號點
則後面是連續下降的。
所以代價是1
我們可以列舉前面的他實際是DP字首和
打表出來就是
然後我們現在要計算多次詢問的答案:
這是實際上是組合數的字首和
而這個實際上可以莫隊!
一個東西滿足莫隊只需要:
向四個方向都是 轉移的
設求解的東西是:
n的變化是很好求解的。
這直接是按照公式定義的
但是這實際上底也是可以同理的
觀察:
它等價於:
按照遞推的轉移式,可以拆解為:
等價於
即:
逆推得到:
這就是莫隊了
注意塊的大小的取捨。
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
inline void read(int &x){
x=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
typedef long long LL;
const int N=1e5+100;
const LL mod=19260817;
LL Quick_Pow(LL x,int k){
LL ret=1;
while(k){
if(k&1)ret=ret*x%mod;
x=x*x%mod;
k/=2;
}
return ret;
}
//
LL powtwo[N+10];
LL fac[N+10];
LL Inv[N+10];
LL inv3;
void Pre(){
inv3=Quick_Pow(3,mod-2);
powtwo[0]=1;
for(int i=1;i<=N;++i)powtwo[i]=powtwo[i-1]*2%mod;
fac[0]=1;
for(int i=1;i<=N;++i)fac[i]=fac[i-1]*i%mod;
Inv[N]=Quick_Pow(fac[N],mod-2);
for(int i=N-1;i>=0;--i)Inv[i]=Inv[i+1]*(i+1)%mod;
}
LL C(int n,int m){
return fac[n]*Inv[n-m]%mod*Inv[m]%mod;
}
//
int Belong[N];
int Sqr;
LL now=1;
int l=1;
int r=1;
inline void rplus(){
now=((3LL*now-C(r,l)*powtwo[l]%mod+C(r,0)*powtwo[0])%mod+mod)%mod;
++r;
}
inline void rdelete(){
now=((now+C(r-1,l)*powtwo[l]%mod-C(r,0)*powtwo[0])%mod+mod)%mod*inv3%mod;
--r;
}
inline void lplus(){
now=(now+C(r,l+1)*powtwo[l]%mod)%mod;
++l;
}
inline void ldelete(){
now=((now-C(r,l)*powtwo[l-1]%mod)%mod+mod)%mod;
--l;
}
struct Query{
int l,r,Id,ans;
}Q[N];
bool cmp(Query A,Query B){
if(Belong[A.l]^Belong[B.l])return A.l<B.l;
if(Belong[A.l]&1)return A.r<B.r;
else return A.r>B.r;
}
bool cmp2(Query A,Query B){
return A.Id<B.Id;
}
int cnt=0;
int Mx;
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
Pre();
int testcase;
read(testcase);
for(int i=1;i<=testcase;++i){
++cnt;
read(Q[cnt].r);
read(Q[cnt].l);
Q[cnt].Id=i;
Mx=max(Mx,Q[cnt].r);
}
Sqr=sqrt(Mx);
for(int i=1;i<=Mx;++i){
Belong[i]=(i-1)/Sqr+1;
}
sort(Q+1,Q+1+cnt,cmp);
for(int i=1;i<=cnt;++i){
if(Q[i].r>=r){
while(r<Q[i].r)rplus();
while(r>Q[i].r)rdelete();
while(l<Q[i].l)lplus();
while(l>Q[i].l)ldelete();
}
else{
while(l<Q[i].l)lplus();
while(l>Q[i].l)ldelete();
while(r<Q[i].r)rplus();
while(r>Q[i].r)rdelete();
}
Q[i].ans=now;
}
sort(Q+1,Q+1+cnt,cmp2);
for(int i=1;i<=cnt;++i){
cout<<Q[i].ans<<'\n';
}
}