NOI.AC CSP-S 模拟 Round 2 T1 Count (组合数学) (容斥)

传送门
题意:给定两个整数 n , m n, m n,m 求 k 元组 ( a 1 , a 2 , … , a k ) (a1,a2,…,ak) (a1,a2,,ak) 的个数,满足 a 1 , a 2 , … , a k a1,a2,…,ak a1,a2,,ak 为正整数
∑ a i = n \sum a_i=n ai=n a 1 , a 2 , … , a k a1,a2,…,ak a1,a2,,ak 均不是 m m m的倍数
n ≤ 1 e 18 , m ≤ 5 e 3 , k ≤ 2 e 3 n\le 1e18,m\le5e3,k\le2e3 n1e18,m5e3,k2e3


首先,如果没有 m m m 的限制,答案为 ( n − 1 k − 1 ) \binom{n-1}{k-1} (k1n1),插板,先全部减去1
考虑 m m m 的限制,发现不好容斥
发现最后的序列一定没有 m 的倍数,并且如果一个数 ≥ m \ge m m ,可以将它表示为 r + k m r+km r+km
于是我们可以将最后的序列抽象为先选好 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak,并强制让 a i < m a_i<m ai<m,再对 a i a_i ai 随便加几个 m m m
枚举 ∑ r i = S \sum r_i = S ri=S r r r 为上文所述的 r r r,那么先选好的 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak 的条件可以抽象为:

  1. ∑ a i = S \sum a_i = S ai=S
  2. a i ≤ m − 1 a_i \le m-1 aim1

然后在对几个 a i a_i ai 加上 m m m 的倍数,假设 ≤ n \le n n 的范围内有 c c c 个 m 的倍数
发现这个过程等价于把 c c c 分到 k 个集合,可以为空,方案数为 ( c + k − 1 k − 1 ) \binom{c+k-1}{k-1} (k1c+k1)
如何求第一个,一下令 m m m m − 1 m-1 m1,考虑容斥,强制 i i i > m >m >m
但如何求强制 i i i > m >m >m 的个数呢,发现这个问题等价于选 i 个位置出来 > m > m >m,然后将 n − i ∗ m n-i*m nim 分配给这 k k k 个集合,最后再将这 i i i 个加上 m m m,于是有
f ( n , m , k ) = ∑ i = 0 k ( − 1 ) i ( k i ) ( n − i ∗ m − 1 k − 1 ) f(n,m,k)=\sum_{i=0}^k(-1)^i\binom{k}{i}\binom{n-i*m-1}{k-1} f(n,m,k)=i=0k(1)i(ik)(k1nim1)

最后的答案
a n s = ∑ i = 0 k f ( i ∗ m + n % m , m − 1 , k ) ∗ ( n m − i + k − 1 k − 1 ) ans=\sum_{i=0}^k f(i*m+n\%m,m-1,k)*\binom{\frac{n}{m}-i+k-1}{k-1} ans=i=0kf(im+n%m,m1,k)(k1mni+k1)
n ≤ 1 e 18 n\le 1e18 n1e18,组合数暴力乘,有些不像 T 1 T1 T1


#include<bits/stdc++.h>
#define cs const
using namespace std;
typedef long long ll;
ll read(){
	ll cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 1e7 + 5;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a, b);}
ll n; int m, k, fac[N], inv[N];
int C(ll n, int m){
	if(n < 0 || m < 0 || n < m) return 0;
	if(n < N) return mul(fac[n], mul(inv[m], inv[n - m]));
	else{
		int ret = inv[m];
		for(ll i = n; i >= n - m + 1; i--) ret = mul(ret, i % Mod);
		return ret;
	}
}
int g(ll n, int m){
	// devide n into m group and the group can be empty
	if(n < 0) return 0;
	return C(n + m - 1, m - 1);
}
int f(int n, int m, int k){ 
	// devide n into m group that the size of each group is less than k
	// can not be empty 
	n -= m; if(n < 0) return 0;
	int ans = 0;
	for(int i = 0; i <= m; i++){
		int ret = mul(g(n - i * k, m), C(m, i));
		(i & 1) ? Add(ans, Mod - ret) : Add(ans, ret); 
	} return ans;
}
int main(){
	n = read(), m = read(), k = read();
	fac[0] = fac[1] = inv[0] = inv[1] = 1;
	for(int i = 2; i < N; i++) fac[i] = mul(fac[i-1], i);
	for(int i = 2; i < N; i++) inv[i] = mul(Mod-Mod/i, inv[Mod%i]);
	for(int i = 2; i < N; i++) inv[i] = mul(inv[i], inv[i-1]);
	// 枚举 < m 的数的和  
	ll r = n % m; int ans = 0;
	for(int i = 0; i < k; i++){
		Add(ans, mul(f(i * m + r, k, m - 1), g((n - r) / m - i, k))); 
	} cout << ans; return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值