【AT987】高橋君【组合数】【莫队】

本文探讨了莫队算法在解决特定组合数学问题中的应用,通过动态转移的方法优化了对大规模数据集的处理效率。针对TTT组询问NNN个相同物品选不超过KKK个的方案数问题,提出了利用莫队算法进行高效求解的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题意: T T T组询问 N N N个相同物品选不超过 K K K个的方案数, T , N ≤ 1 e 5 T,N \leq 1e5 T,N1e5

f ( x , y ) = ∑ i = 0 y C x i f(x,y)=\sum_{i=0}^{y}C_x^i f(x,y)=i=0yCxi即所求

直接求并没有很好的性质

但我们发现: f ( x , y ) = 2 f ( x − 1 , y ) − C x − 1 y f(x,y)=2f(x-1,y)-C_{x-1}^y f(x,y)=2f(x1,y)Cx1y,即乘以二减去最后一个等于下一行

C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n-1}^m+C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1,只有最后一个出现一次,其余出现两次

f ( x , y ) 到 f(x,y)到 f(x,y) f ( x , y ± 1 ) f(x,y \pm 1) f(x,y±1)很好转移

然后跑莫队即可

复杂度 O ( N T ) O(N \sqrt{T}) O(NT )

在这里插入图片描述

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#define MAXN 100005
#define MAX 100000
inline int read()
{
	int ans=0;
	char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
const int MOD=1e9+7;
using namespace std;
typedef long long ll;
inline int qpow(int a,int p)
{
	int ans=1;
	while (p)
	{
		if (p&1) ans=(ll)ans*a%MOD;
		a=(ll)a*a%MOD,p>>=1;
	}
	return ans;
}
int fac[MAXN],inv[MAXN];
void init()
{
	fac[0]=1;
	for (int i=1;i<=MAX;i++) fac[i]=(ll)fac[i-1]*i%MOD;
	inv[MAX]=qpow(fac[MAX],MOD-2);
	for (int i=MAX-1;i>=0;i--) inv[i]=(ll)inv[i+1]*(i+1)%MOD;
}
inline int C(const int& n,const int& m){return (ll)fac[n]*inv[m]%MOD*inv[n-m]%MOD;}
int len;
struct query{int x,y,pos;}q[MAXN];
int res[MAXN];
inline bool operator <(const query& a,const query& b)
{
	if (a.x/len==b.x/len) return a.y<b.y;
	return a.x<b.x;
}
int main()
{
	init();
	int T,n=0;
	T=read();
	for (int i=1;i<=T;i++) n=max(n,q[i].x=read()),q[i].y=read(),q[i].pos=i;
	len=sqrt((ll)n*n/T);
	sort(q+1,q+T+1);
	int x=q[1].x,y=q[1].y,ans=0;
	for (int i=0;i<=y;i++) ans=(ans+C(x,i))%MOD;
	res[q[1].pos]=ans;
	for (int i=2;i<=T;i++)
	{
		while (x<q[i].x) ans=(ans*2ll-C(x,y)+MOD)%MOD,++x;
		while (x>q[i].x) --x,ans=(ll)inv[2]*(ans+C(x,y))%MOD;
		while (y<q[i].y) ++y,ans=(ans+C(x,y))%MOD;
		while (y>q[i].y) ans=(ans+MOD-C(x,y))%MOD,--y;
		res[q[i].pos]=ans;
	}
	for (int i=1;i<=T;i++) printf("%d\n",(res[i]+MOD)%MOD);
	return 0;
}

由此题可知,莫队不只能处理区间,凡是方便相邻转移的函数都可以考虑莫队

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值