2018.10.24【NOIP練習】Leo的組合數問題(組合數學)(莫隊)

傳送門


解析:

老是忍不住要吐槽一句,這題面也太長了吧。。。php

首先,確定要把咱們要求的東西推成一個式子,否則怎麼可能作題。。。html

f i f_i 表示當前以 i i 爲最大值的方案數。
那麼 i i 以後的數顯然是單調遞減且連續,方案數爲 1 1
考慮前 i 1 i-1 個位置,放法數就是 j = 1 i 1 f j \sum_{j=1}^{i-1}f_j
因此咱們如今獲得遞推式 f i = j = 1 i 1 f j f_i=\sum_{j=1}^{i-1}f_j f 1 = 1 f_1=1 c++

轉換一下 f i = j = 1 i 1 f j = j = 1 i 2 f j + f i 1 = 2 f i 1 f_i=\sum_{j=1}^{i-1}f_j=\sum_{j=1}^{i-2}f_j+f_{i-1}=2f_{i-1} git

因此獲得通項公式 f i = 2 i 1 f_i=2^{i-1} web

那麼詢問的答案就是 Q ( n , m ) = i = 1 n C m i × 2 i 1 Q(n,m)=\sum_{i=1}^{n}C_{m}^i\times 2^{i-1}
數據範圍 1 e 5 1e5 ,果斷莫隊。
l = n , r = m l=n,r=m
咱們只須要找到根據 l , r l,r 更新答案的較小複雜度方法就好了
幸運的是,這個式子在 l ± 1 l\pm 1 r ± 1 r \pm 1 的時候都有 O ( 1 ) O(1) 的轉移方式。app

首先 l l 的轉移:
Q ( l 1 , r ) = Q ( l , r ) C r l × 2 l 1 Q(l-1,r)=Q(l,r)-C_r^l\times 2^{l-1} Q ( l + 1 , r ) = Q ( l , r ) + C r l + 1 × 2 l Q(l+1,r)=Q(l,r)+C_r^{l+1}\times 2^{l}
這個轉移十分顯然,就是直接根據表達式轉移。svg

r r 的轉移須要一點技巧:
怎麼求 Q ( l , r + 1 ) = i = 1 l C r + 1 i × 2 i 1 Q(l,r+1)=\sum_{i=1}^lC_{r+1}^{i}\times 2^{i-1} spa

因爲帕斯卡三角形上的組合數遞推公式 C n + 1 m = C n m + C n m 1 C_{n+1}^m=C_n^m+C_n^{m-1} 。咱們把上面這個式子拆開獲得 i = 1 l ( C r i + C r i 1 ) × 2 i 1 = i = 1 l C r i × 2 i 1 + 2 × i = 1 l 1 C r i × 2 i 1 + C r 0 × 2 0 \sum_{i=1}^{l}(C_r^i+C_r^{i-1})\times 2^{i-1}=\sum_{i=1}^{l}C_r^i\times 2^{i-1}+2\times\sum_{i=1}^{l-1}C_r^{i}\times2^{i-1}+C_r^0\times 2^0 .net

因此其實就是 Q ( l , r + 1 ) = Q ( l , r ) + 2 × Q ( l 1 , r ) + C r 0 × 2 0 Q(l,r+1)=Q(l,r)+2\times Q(l-1,r)+C_r^0\times 2^0
把剛纔退出來的 Q ( l 1 , r ) Q(l-1,r) 的式子代進來獲得
Q ( l , r + 1 ) = 3 × Q ( l , r ) C r l × 2 l + C r 0 × 2 0 Q(l,r+1)=3\times Q(l,r)-C_r^l\times 2^l+C_r^0\times 2^0
利用這個式子能夠推出 r 1 r-1 的狀況: Q ( l , r 1 ) = Q ( l , r ) + C r 1 l × 2 l C r 0 × 2 0 3 Q(l,r-1)=\frac{Q(l,r)+C_{r-1}^l\times 2^l-C_r^0\times 2^0}{3} code

那麼就能夠上莫隊了。

trick:

注意能將 r r 變大的時候儘可能先移動 r r ,不然先移動 l l ,避免轉移通過非法組合數就 g g gg 了。


代碼:

#include<bits/stdc++.h>
using namespace std;
#define re register
#define gc getchar
#define pc putchar
#define cs const
 
inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline void outint(long long a){
	static char ch[23];
	if(a==0)pc('0');
	while(a)ch[++ch[0]]=a-a/10*10,a/=10;
	while(ch[0])pc(ch[ch[0]--]^48);
}

cs int N=100005;
cs int mod=19260817;

int fac[N],inv[N],ifac[N];
int pow2[N];
inline int C(int n,int m){
	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int l=1,r=1,now=1;
inline void ll(){now=(1ll*now-1ll*C(r,l)*pow2[l-1]%mod+mod)%mod;--l;}
inline void lr(){now=(1ll*now+1ll*C(r,l+1)*pow2[l]%mod)%mod;++l;}
inline void rl(){now=(1ll*now+1ll*C(r-1,l)*pow2[l]%mod-1ll*C(r,0)*pow2[0]%mod+mod)%mod*inv[3]%mod;--r;}
inline void rr(){now=(3ll*now-1ll*C(r,l)*pow2[l]%mod+1ll*C(r,0)*pow2[0]%mod+mod)%mod;++r;}

struct Query{int l,r,id;}Q[N];
int ans[N],block[N],B,bcnt;
inline bool cmp(cs Query &a,cs Query &b){
	if(block[a.l]^block[b.l])return block[a.l]<block[b.l];
	return (block[a.l]&1)^(a.r>b.r);
}
int n,m,q;
int maxn;
signed main(){
	fac[0]=fac[1]=inv[0]=inv[1]=ifac[0]=ifac[1]=pow2[0]=1;
	pow2[1]=2;
	for(int re i=2;i<N;++i){
		fac[i]=1ll*fac[i-1]*i%mod;
		inv[i]=1ll*(mod-mod/i)*inv[mod-mod/i*i]%mod;
		ifac[i]=1ll*ifac[i-1]*inv[i]%mod;
		pow2[i]=(pow2[i-1]<<1)%mod;
	}
	q=getint();
	for(int re i=1;i<=q;++i){
		Q[i].id=i;
		Q[i].r=getint();
		Q[i].l=getint();
		maxn=max(Q[i].r,maxn);
	}
	B=sqrt(maxn);bcnt=1;
	for(int re i=1;i<=maxn;++i){
		block[i]=bcnt;
		if(i%B==0)++bcnt;
	}
	sort(Q+1,Q+q+1,cmp);
	for(int re i=1;i<=q;++i){
		if(Q[i].r>=r){
			while(r<Q[i].r)rr();
			while(r>Q[i].r)rl();
			while(l<Q[i].l)lr();
			while(l>Q[i].l)ll();
		}
		else{
			while(l<Q[i].l)lr();
			while(l>Q[i].l)ll();
			while(r>Q[i].r)rl();
			while(r<Q[i].r)rr();
		}
		ans[Q[i].id]=now;
	}
	for(int re i=1;i<=q;++i)outint(ans[i]),pc('\n');
	return 0;
}
相關文章
相關標籤/搜索