AT5169 Candy Retribution

题号是洛谷题号。

前置知识:i=1nai=m{anN} 方案数为 Cn+m1n1i=1naim 方案数为 Cn+mn,后一项的证明就是考虑有 n 个隔板和 m 个物体隔板,然后隔出来有 n+1 个物品,最后一项物品舍弃掉即可。

{an} 降序排列后为 {bn},要统计 bm=bm+1 不好统计,因为你不知道 bm 有多少个,但是可以统计 bm>bm+1 即非法序列个数。

首先将 ai[L,R] 弱化成 aiR,就是差分一下 Solve(R)-Solve(L-1)

枚举 bm 的值 x,设 f(s,x,y) 表示当前序列总和小于等于 sm 个数大于等于 x,剩余 nm 个数小于 x 的方案数,那么 bm=x 时这一块的非法序列个数就是 f(s,x,x)f(s,x+1,x)

考虑如何求 f(s,x,y),先选 m 个位置作为 x 的位置,剩余 nm 位置小于 y,但是注意到隔板法不好求对每个数上界和下界(非 0)有限制的方案数,于是考虑容斥,枚举 i[0,nm] 表示 nm 个位置中至少有 i 个位置 >=y,将这 i 个位置和之前的 x 的数全部从和当中减掉,新和就是 smxiy,然后又要求减掉后的序列和小于等于 smxiy,于是有:

f(s,x,y)=Cnmi=0nm(1)iCnmiCsmxiy+nn

因此最后非法序列个数就是 x=1mf(s,x,x)f(s,x+1,x),总序列个数 Cn+sn 减去即可。

由于要求 smxiy0,而 iy 成倍数增长,所以实际复杂度为调和级数。

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:AT5169 Candy Retribution
	Date:2022/9/25
========= Plozia =========
*/

#include <bits/stdc++.h>
#define int long long
typedef long long LL;

const int MAXN = 6e5 + 5;
const LL P = 1e9 + 7;
int n, m, l, r;
LL fact[MAXN], inv[MAXN];

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
	return sum * fh;
}
LL ksm(LL a, LL b = P - 2, LL p = P)
{
	LL s = 1 % p;
	for (; b; b >>= 1, a = a * a % p)
		if (b & 1) s = s * a % p;
	return s;
}
LL C(LL n, LL m) { return fact[n] * inv[m] % P * inv[n - m] % P; }

LL f(int x, int y, int s)
{
	LL sum = 0;
	for (int i = 0; i <= n - m; ++i)
	{
		if (1ll * s - m * x - i * y < 0) return sum * C(n, m) % P;
		sum = (sum + ((i & 1) ? -1 : 1) * C(n - m, i) * C(1ll * s - m * x - i * y + n, n) % P) % P;
		sum = (sum + P) % P;
	}
	return sum * C(n, m) % P;
}

LL Solve(int s)
{
	LL sum = 0;
	for (int i = 1; i <= s; ++i)
		sum = ((sum + f(i, i, s) - f(i + 1, i, s)) % P + P) % P;
	return (C(s + n, n) % P - sum + P) % P;
}

signed main()
{
	n = Read(), m = Read(), l = Read(), r = Read();
	fact[0] = 1; for (int i = 1; i <= 600000; ++i) fact[i] = fact[i - 1] * i % P;
	inv[600000] = ksm(fact[600000]); for (int i = 599999; i >= 0; --i) inv[i] = (i + 1) * inv[i + 1] % P;
	printf("%lld\n", ((Solve(r) - Solve(l - 1)) % P + P) % P); return 0;
}
posted @   Plozia  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示