HDU 6397(2018多校第8场1001) Character Encoding 容斥

听了杜教的直播后知道了怎么做,有两种方法,一种构造函数(现在太菜了,听不懂,以后再补),一种容斥原理。

知识补充1:若x1,x2,.....xn均大于等于0,则x1+x2+...+xn=k的方案数是C(k+m-1,m-1)种(貌似紫书上有,记不太清了)。

知识补充2:若限制条件为n(即x1,x2....xn均小于n,假设有c个违反,则把k减掉c个n(相当于把c个超过n的数也变成大于等于0的),就可以套用知识1的公式了。

则最后的答案为sum( (-1)^c * C(m , c) * C(m-1+k-n*c , m-1) );

这个题貌似要预处理出乘法逆元,不然会TLE。我的lucas定理做法超时了。。。

借鉴了杜教链接中的代码,O(n)时间预处理:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL mod=998244353;
const int maxn=203000;
LL f[maxn],fv[maxn];//f是阶乘,fv是乘法逆元
LL quick_power(LL a,LL b){
	LL ans=1;
	for(;b;b>>=1){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
	}
	return ans;
}
void init(){//初始化
	f[0]=1;
	for(LL i=1;i<maxn;i++)
		f[i]=(f[i-1]*i)%mod;
	fv[maxn-1]=quick_power(f[maxn-1],mod-2);
	for(LL i=maxn-1;i>0;i--){
		fv[i-1]=fv[i]*i%mod;
	}
}
LL C(LL n,LL m){//这样可以O(1)计算出组合数
	if(n<0||m<0||n<m)return 0;
	return f[n]*(fv[m])%mod*fv[n-m]%mod;
}
int main(){
	LL n,m,k,ans;
	int T;
	init();
	scanf("%d",&T);
	while(T--){
		scanf("%lld%lld%lld",&n,&m,&k);
		LL ans=0;
		for(int c=0;c*n<=k;c++){//容斥
			if(c&1)ans=(ans-C(m,c)*C(k-c*n-1+m,m-1)%mod+mod)%mod;
			else ans=(ans+C(m,c)*C(k-c*n-1+m,m-1)%mod)%mod;
		}
		printf("%lld\n",ans);
	}
}

  还是附上超时代码,单次查询是O(lgn)的,如果查询次数比较少可以用这个,可以当作模板。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL mod=998244353;
LL power(LL a){
	if(a&1)return -1;
	return 1;
}
LL quick_power(LL a,LL b){
	LL ans=1%mod;
	while(b){
		if(b&1){
			ans=ans*a%mod;
			b--;
		}
		b>>=1;
		a=a*a%mod;
	}
	return ans;
}
LL C(LL n,LL m){
	if(m>n)return 0;
	LL ans=1;
	for(int i=1;i<=m;i++){
		LL a=(n+i-m)%mod;
		LL b=i%mod;
		ans=ans*(a*quick_power(b,mod-2)%mod)%mod;
	}
	return ans;
}
LL lucas(LL n,LL m){
	if(m==0)return 1;
	return C(n%mod,m%mod)*lucas(n/mod,m/mod)%mod;
}
int main(){
	LL n,m,k,ans;
	int T;
	scanf("%d",&T);
	while(T--){
		ans=0;
		scanf("%lld%lld%lld",&n,&m,&k);
		for(LL i=0;i*n<=k;i++){
			ans=(ans+(((power(i)*lucas(m,i))%mod)*lucas(m-1+k-n*i,m-1))%mod)%mod;
		}
		printf("%lld\n",ans);
	}
}

  

原文地址:https://www.cnblogs.com/pkgunboat/p/9484230.html

时间: 2024-10-09 09:21:48

HDU 6397(2018多校第8场1001) Character Encoding 容斥的相关文章

hdu 5288||2015多校联合第一场1001题

http://acm.hdu.edu.cn/showproblem.php?pid=5288 Problem Description OO has got a array A of size n ,defined a function f(l,r) represent the number of i (l<=i<=r) , that there's no j(l<=j<=r,j<>i) satisfy ai mod aj=0,now OO want to know ∑i

HDU 6395(2018多校第7场1010)Sequence

不久前做过POJ3070,所以知道这题要用矩阵快速幂优化,但是这个题的递推公式中有一项?p/n?,场上就不会了... 下来才知道要用分块矩阵快速幂,因为?p/n?最多有2√p块,可以对每一块使用快速幂,复杂度(应该)为lgn*√p. 每一块的范围可以在O(1)的时间内求出,范围为x到min(n,p/(p/x)),具体证明lyd的进阶指南上有... 附上代码: #include<cstdio> #include<algorithm> #include<cstring> u

2014多校第五场1001 || HDU 4911 Inversion (归并求逆序数)

题目链接 题意 : 给你一个数列,可以随意交换两相邻元素,交换次数不超过k次,让你找出i < j 且ai > aj的(i,j)的对数最小是多少对. 思路 : 一开始想的很多,各种都想了,后来终于想出来这根本就是求逆序数嘛,可以用归并排序,也可以用树状数组,不过我们用树状数组做错了,也不知道为什么.求出逆序数来再减掉k次,就可以求出最终结果来了.求逆序数链接1,链接2 1 #include <stdio.h> 2 3 int left[250003], right[250003];

HDU 5768Lucky7(多校第四场)容斥+中国剩余定理(扩展欧几里德求逆元的)+快速乘法

地址:http://acm.hdu.edu.cn/showproblem.php?pid=5768 Lucky7 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 754    Accepted Submission(s): 279 Problem Description When ?? was born, seven crows flew

杭电2018多校第四场(2018 Multi-University Training Contest 4) 1005.Problem E. Matrix from Arrays (HDU6336) -子矩阵求和-规律+二维前缀和

6336.Problem E. Matrix from Arrays 不想解释了,直接官方题解: 队友写了博客,我是水的他的代码 ------>HDU 6336 子矩阵求和 至于为什么是4倍的,因为这个矩阵是左上半边有数,所以开4倍才能保证求的矩阵区域里面有数,就是图上的红色阴影部分,蓝色为待求解矩阵. 其他的就是容斥原理用一下,其他的就没什么了. 代码: 1 //1005-6336-矩阵求和-二维前缀和+容斥-预处理O(1)查询输出 2 #include<iostream> 3 #in

hdu 2018 多校 第五场

1002 小洛洛 开场挂了N发插入排序的我( 最后还是要靠小洛洛写暴力 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <cmath> 5 #include <cstdlib> 6 #include <algorithm> 7 using namespace std; 8 9 const int N = 12; 10 11 int mi

2018多校第十场 HDU 6430 线段树合并

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6430 题意:一棵树上每个节点权值为v[i],每个节点的heard值是:以它为LCA的两个节点的GCD的最大值,要求输出每个节点的heard值. 题解:权值范围是[1, 1e5],1e5内数因子最多不超过200个,对每个点建一颗线段树,维护每个点的因子,dfs过程中由下往上合并线段树并更新答案. 1 #include <bits/stdc++.h> 2 using namespace std; 3

HDU contest808 ACM多校第7场 Problem - 1008: Traffic Network in Numazu

首先嘚瑟一下这场比赛的排名:59 (第一次看到这么多 √ emmmm) 好了进入正文QAQ ...这道题啊,思路很清晰啊. 首先你看到树上路径边权和,然后还带修改,不是显然可以想到 树剖+线段树 维护重链么? 然后你再看啊,这是一个连通图,然后有 n 个点 n 条边,于是很显然会有一个环(然后就构成了一个 仙人掌 ...不过我并不了解仙人掌) 然后你再看!这里只会有一个环,我们假设没有这个环,那么这就是一道 树剖 模板题,那么我们可不可以特殊地,让这个环当根,除这个环以外的其他节点来简单 树剖

双01字典树最小XOR(three arrays)--2019 Multi-University Training Contest 5(hdu杭电多校第5场)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6625 题意: 给你两串数 a串,b串,让你一一配对XOR使得新的 C 串字典序最小. 思路: 首先这边有两个问题: 1. 我要怎么知道这两个数配对是最优的:一开始我也不明白(以为选择会有后效性),其实很简单,对 a 里的一个X,在 b 的01树里跑到的最优解Y也一定就是 b 的这个Y在 a 的01树里跑到的最优解X. 2. 如果现在两颗树里跑到的点的下一个只有一种选择的话,肯定就接着跑,但是如果现在