贴个网址 :
ZJNU-2022暑期专题-组合数学 - Virtual Judge (vjudge.net)
目录
知识点:
求逆元性价比最高公式
先求出尾巴的inv,然后层层往前回去。
rep(i,2,N-5){
f[i]=f[i-1]*i%p;
}
inv[N-5]=fastpower(f[N-5],p-2,p);
nep(i,N-6,2){
inv[i]=inv[i+1]*(i+1)%p;
}
错排
//错排递推公式
d[1]=0;d[2]=1;d[3]=2;
//d表示全错排n个数的情况有多少
rep(i,4,N){
d[i]=((i-1)*(d[i-1]+d[i-2]))%p;
}
//故5个数错排3个,就是C(3,5)*d[3];
Lucas
//求组合,当q次询问余数p(<1e6且质数)的值会改变的时候使用。
int lucas(int n,int m){
if (!m) return 1;
return c(n%p,m%p)*lucas(n/p,m/p)%p;
}
例题:
E - Shinyruo and KFC
大意:n个食物,每个食物ai个,m个队伍,每个队伍每种食物最多吃一个,求队伍分别为1~m的时候,分配食物吃完的不同情况。
学长说这是一道铜牌题。
这道题还有个题面,就是所有ai的和不超过1e5,而n也是1e5,那么不同的ai最多只有sqrt(1e5)个,所以抽屉原理,会有一堆重复的,这时候就可以unordered_map存进去,时间复杂度就变成了,n*sqrt(1e5)*快速幂logn,就能过了。
总结:铜牌题更注重对不同题目数据的处理。
#include <bits/stdc++.h>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define nep(i,r,l) for (int i=r;i>=l;i--)
#define pii pair<int,int>
#define int long long
#define CIO std::ios::sync_with_stdio(false)
using namespace std;
const int N=2e5+5;
const int p=998244353;
int a[N],t[N],tn[N];
int f[N],inv[N],finv[N];
unordered_map <int,int> mp;
int fastpower(int a,int x,int p){
int ans=1;
while (x){
if (x&1) ans=(ans*a)%p;
x=x/2;
a=(a*a)%p;
}
return ans%p;
}
void Int(){
f[0]=inv[0]=f[1]=inv[1]=finv[0]=finv[1]=1;
rep(i,2,N-5){
f[i]=f[i-1]*i%p;
}
inv[N-5]=fastpower(f[N-5],p-2,p);
nep(i,N-6,2){
inv[i]=inv[i+1]*(i+1)%p;
}
}
int c(int m,int n){
if (m>n) return 0;
return f[n]*inv[n-m]%p*inv[m]%p;
}
void work(){
Int();
int n,m;cin>>n>>m;
int ma=0;
rep(i,1,n){
cin>>a[i];
mp[a[i]]++;
ma=max(ma,a[i]);
}
int ans=0;
unordered_map<int,int>::iterator it;
int cnt=0;
for(it=mp.begin();it!=mp.end();it++){
t[++cnt]=it->second;
tn[cnt]=it->first;
}
rep(i,1,m){
if (i<ma) cout<<0<<endl;
else{
ans=1;
rep(j,1,cnt){
if (t[j]!=0){
int zhi=fastpower(c(tn[j],i),t[j],p);
if (zhi!=0){
ans=ans*zhi%p;
}
}
}
cout<<ans<<endl;
}
}
}
signed main(){
CIO;
work();
return 0;
}
G - 硬币购物
共有4种硬币。面值分别为c1,c2,c3,c4。
某人去商店买东西,去了n次,对于每次购买,他带了 di 枚 i种硬币,想购买 s 的价值的东西。请问每次有多少种付款方法。
这是一道dp+容斥的一道题,如果没有数量限制,就是一道完全背包的板子题目,我们假设一个超过限制,其他的就是完全背包,然后假设四遍,最后减掉,但发现会多减掉,这就是容斥原理了,然后再继续处理,暴力+-就好了,只有4种硬币。
#include <bits/stdc++.h>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define nep(i,r,l) for (int i=r;i>=l;i--)
#define pii pair<int,int>
#define int long long
#define CIO std::ios::sync_with_stdio(false)
using namespace std;
const int N=2e5+5;
const int p=998244353;
int c[10],dp[N];
int d[10],s;
int sum(int idx){
return c[idx]*(d[idx]+1);
}
void work(){
int n;
rep(i,1,4){
cin>>c[i];
}
dp[0]=1;
rep(i,1,4){
for (int j=c[i];j<N;j++){
dp[j]+=dp[j-c[i]];
}
}
cin>>n;
rep(i,1,n){
rep(j,1,4){
cin>>d[j];
}
cin>>s;
int ans=dp[s];
if (s>=sum(1)) ans-=dp[s-sum(1)];
if (s>=sum(2)) ans-=dp[s-sum(2)];
if (s>=sum(3)) ans-=dp[s-sum(3)];
if (s>=sum(4)) ans-=dp[s-sum(4)];
if (s>=sum(1)+sum(2)) ans+=dp[s-sum(1)-sum(2)];
if (s>=sum(1)+sum(3)) ans+=dp[s-sum(1)-sum(3)];
if (s>=sum(1)+sum(4)) ans+=dp[s-sum(1)-sum(4)];
if (s>=sum(2)+sum(3)) ans+=dp[s-sum(2)-sum(3)];
if (s>=sum(2)+sum(4)) ans+=dp[s-sum(2)-sum(4)];
if (s>=sum(3)+sum(4)) ans+=dp[s-sum(3)-sum(4)];
if (s>=sum(1)+sum(2)+sum(3)) ans-=dp[s-sum(1)-sum(2)-sum(3)];
if (s>=sum(1)+sum(3)+sum(4)) ans-=dp[s-sum(1)-sum(3)-sum(4)];
if (s>=sum(1)+sum(2)+sum(4)) ans-=dp[s-sum(1)-sum(2)-sum(4)];
if (s>=sum(2)+sum(3)+sum(4)) ans-=dp[s-sum(2)-sum(3)-sum(4)];
if (s>=sum(1)+sum(2)+sum(3)+sum(4)) ans+=dp[s-sum(1)-sum(2)-sum(3)-sum(4)];
cout<<ans<<endl;
}
}
signed main(){
CIO;
work();
return 0;
}