1. 程式人生 > >學習筆記:楊輝三角形上莫隊(組合數莫隊)(LULU胡策)

學習筆記:楊輝三角形上莫隊(組合數莫隊)(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隨手組的數學題
有點毒瘤


我們發現由於題目的性質:
所以選出一些數的代價是相同的:為 C m k C_{m}^{k}
但是對於一段數它的代價是可以DP的
我們列舉當前最大的值的位置:
如果最大值在:第i號點
則後面是連續下降的。
所以代價是1
我們可以列舉前面的他實際是DP字首和
打表出來就是 O
( 2 n ) O(2^{n})

然後我們現在要計算多次詢問的答案:
i
= 1 n C m i 2 i 1 \sum_{i=1}^{n}C_{m}^{i}2^{i-1}

這是實際上是組合數的字首和
而這個實際上可以莫隊!
一個東西滿足莫隊只需要:
向四個方向都是 O ( 1 ) O(1) 轉移的
設求解的東西是: S m n S_{m}^{n}
n的變化是很好求解的。
S m n 1 = S m n C m n 2 n 1 S_{m}^{n-1}=S_{m}^{n}-C_{m}^{n}*2^{n-1}
S m n + 1 = S m n + C m n + 1 2 n S_{m}^{n+1}=S_{m}^{n}+C_{m}^{n+1}*2^{n}
這直接是按照公式定義的
但是這實際上底也是可以同理的
觀察: S m + 1 n S_{m+1}^{n}
它等價於: i = 1 n C m + 1 i 2 i 1 \sum_{i=1}^{n} C_{m+1}^{i}*2^{i-1}
按照遞推的轉移式,可以拆解為:
i = 1 n ( C m i + C m i 1 ) 2 i 1 \sum_{i=1}^{n}(C_{m}^{i}+C_{m}^{i-1})*2^{i-1}
等價於 S m + 1 n = i = 1 n 1 3 C m i + C m 0 2 0 + C m n 2 n 1 S_{m+1}^{n}=\sum_{i=1}^{n-1}3*C_{m}^{i}+C_{m}^{0}*2^{0}+C_{m}^{n}*2^{n-1}
即: S m + 1 n = 3 S m n C m n 2 n + C m 0 2 0 S_{m+1}^{n}=3*S_{m}^{n}-C_{m}^{n}*2^{n}+C_{m}^{0}*2^{0}
逆推得到: S m 1 n = S m n + C m 1 n C m 0 2 0 3 S_{m-1}^{n}=\frac{S_{m}^{n}+C_{m-1}^{n}-C_{m}^{0}*2^{0}}{3}
這就是莫隊了
注意塊的大小的取捨。

#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';
	}
}