学习笔记:杨辉三角形上莫队(组合数莫队)(LULU胡策)

版权声明:LeoJAM Presents https://blog.csdn.net/fcb_x/article/details/83309461

与唐林康的决战在即,面筋哥需要一件压场子的终极武器。
面筋哥手上有 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';
	}
}

猜你喜欢

转载自blog.csdn.net/fcb_x/article/details/83309461
今日推荐