10.19练习题目总结
[NOIP2011 提高组] 计算系数
组合计数模板题,要去求\((by+ax)^k\)的第\(x^n*y^m\)项的系数众所周知\((a+b)^n=\sum_{i=0}^nC_n^ia^ib^{n-i}\)
所以答案即为\(C_k^m*a^n*b^m\)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e6+10;
const ll mod=10007;
ll n,m,a,b,k,l,ans,len;
ll jie[N],jie_ni[N];
ll ppow(ll n,ll m){ll s=1;while(m){if(m&1) s=s*n%mod;n=n*n%mod;m/=2;}return s;}
ll C(int n,int m){
if(n<m||n<0||m<0)
return 0;
return jie[n]*jie_ni[m]%mod*jie_ni[n-m]%mod;
}
int main(){
cin>>a>>b>>k>>n>>m;
jie[0]=1;
for(int i=1;i<=1000;i++)
jie[i]=jie[i-1]*i%mod;
jie_ni[1000]=ppow(jie[1000],mod-2);
for(int i=1000;i>0;i--)
jie_ni[i-1]=jie_ni[i]*i%mod;
cout<<c(k,m)%mod*ppow(a,n)%mod*ppow(b,m)%mod;
}
combination
Lucas定理模板题
在求组合数是当模数很小时应用Lucas定理去递推求c
即
\(C_n^m\equiv C_{n/p}^{m/p}C_{n\%p}^{m\%p}(\%p)\)
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define se second
#define fi first
using namespace std;
const int N=1e5+10;
const int mod=10007;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll maxx=mod-1;
ll n,m,k,ans,cnt,t;
ll a[N],b[N];
ll jie[N],ni[N];
ll read(){ll x;cin>>x;return x;}
struct zzz{
ll x,y,v;
}e[N];
int cmp(zzz a,zzz b){
return a.v<b.v;
}
ll ppow(ll n,ll m){ll s=1;while(m){if(m&1)s=s*n%mod;n=n*n%mod;m/=2;}return s;}
ll c(ll n,ll m){
if(n<m||n<0||m<0)
return 0;
return jie[n]*ni[m]%mod*ni[n-m]%mod;
}
ll lu(ll n,ll m){
if(n<0||m<0)return 0;
if(m==0)return 1;
if(n<mod&&m<mod)return c(n,m);
return c(n%mod,m%mod)*lu(n/mod,m/mod)%mod;
}
int main(){
jie[0]=1;
for(int i=1;i<=maxx;i++)jie[i]=(jie[i-1]*i)%mod;
ni[maxx]=ppow(jie[maxx],mod-2);
for(int i=maxx;i;i--)ni[i-1]=(ni[i]*i)%mod;
cin>>t;
while(t--){
ll x,y;
cin>>x>>y;
cout<<lu(x,y)<<'\n';
}
}
[abc433F] 1122 Subsequence 2
题意
给定一个由数字组成的字符串 SS。
如果字符串 TT 满足以下所有条件,则称其为 1122-字符串。(定义与问题 C 相同)
- T 是一个非空的数字字符串。
- ∣T∣为偶数,其中 ∣T∣ 表示字符串 T 的长度。
- T 中从第 1 个字符到第 ∣\(T\over2\)∣ 个字符都是相同的数字。
- T 中从第 ( ∣\(T\over2\)∣+1) 个字符到第 ∣T∣ 个字符都是相同的数字。
- 将 1 加到 T 的第 1 个字符上的数字,可以得到 T 的第 ∣T∣ 个字符上的数字。
计算 SS 的所有子序列(不一定连续)中,满足 1122-字符串条件的数量,并对 998244353 取模。
两个子序列如果从不同的位置提取,即使作为字符串相同也会被分别计数。
题解
容易发现的是需要计算进ans的就是枚举0-8的时候因为9在+1变为10不满足条件,
暴力的写法我们可以钦定当前枚举到i号数字的答案为必须以s[i]号数字为最后一个较小数字的方案数,有定义可知可以枚举j表示当前的s[i]的长度即 ∣\(T\over2\)∣,同样的我们也需要向后去找j个s[i]+1,
我们可以先pos数组对于每个数字去求前缀数量这样在求s[i]+1的数量时只需用pos[n]-pos[i]即可
所以对于每个i对应数字为x\(ans=\sum_{j=0}^{pos[i][x]-1}C_{pos[i][x]-1}^jC_{pos[n][x+1]-pos[i][x+1]}^j\)
string s;
cin>>s;
n=s.size();
s=' '+s;
for(int i=1;i<=n;i++){
for(int j=0;j<=9;j++){
pos[i][j]=pos[i-1][j];
}
pos[i][s[i]-'0']++;
}
for(int i=1;i<=n;i++){
if(s[i]=='9')continue;
for(int j=0;j<=pos[i][s[i]-'0']-1;j++){
(ans=ans+c(pos[i][s[i]-'0']-1,j)*c(pos[n][s[i]-'0'+1]-pos[i][s[i]-'0'+1],j+1)%mod)%=mod;
}
}
进一步用范德蒙德卷积变式\(\sum_k^nC_n^kC_m^k=C_{n+m}^n\)优化掉j
原先的式子可优化为\(\sum C_{k-1}^{n-1}C_m^k=C_{n+m-1}^n\)
string s;
cin>>s;
n=s.size();
s=' '+s;
for(int i=1;i<=n;i++){
for(int j=0;j<=9;j++){
pos[i][j]=pos[i-1][j];
}
pos[i][s[i]-'0']++;
}
for(int i=1;i<=n;i++){
if(s[i]=='9')continue;
(ans+=c(pos[i][s[i]-'0']+pos[n][s[i]-'0'+1]-pos[i][s[i]-'0'+1]-1,pos[i][s[i]-'0']))%=mod;
}
[ABC154F] Many Many Paths
题目描述
有一个二维平面。站在这个平面上的すぬけ君每次操作可以向 x 轴正方向移动 1,或者向 y 轴正方向移动 1。
定义函数 f(r,c) 如下:
- f(r,c) :
给定整数r1, r2, c1, c2。请计算所有满足r1≤i≤r2 且 c1≤j≤c2 的整数对 (i,j) 的 f(i,j*) 之和,并对 1e9+7 取模。
题解
首先对于点\((x,y)\)从\((0,0)\)只能向上向右走到它的方案数为\(C_{x+y}^{x}\)所以可以枚举每个区间内的点复杂度\(O(n^2)\)
ll ans,x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
for(int x=x1;x<=x2;x++){
for(int y=y1;y<=y2;y++){
(ans+=c(x+y,y))%=mod;
}
}
容易发现\(C_{x+y}^y\)可以用上指标求和优化掉所以就做完了
由于组合数中出现了(x+y+1)所以预处理组合数时应开大一点到3e6
ll x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
for(int x=x1;x<=x2;x++){
(ans+=(c(x+y2+1,y2)-c(x+y1,y1-1)+mod)%mod)%=mod;
}
[清华集训2016] 组合数问题
题意
给定 n,m和 k,对于所有的 0≤i≤n,0≤j≤min(i,m)有多少对 (i,j) 满足 Cij 是 k 的倍数。
答案对 1e9+7取模。
题解
因为k较小考虑用Lucas,根据Lucas定理可以知道
要想\(C_n^m\)模k=0那么\(C_{n/p}^{m/p}和C_{n\%p}^{m\%p}\)中至少一个为k的倍数,学过Lucas定理的肯定知道这实际上是把n,m
k进制分解在逐个去做组合数,即是\(n,m\)在\(k\)进制下的每一位一一对应的组合数乘起来:设\(n\)的\(k\)进制下是\(n_0,n_1,n_2...\),\(m\)的\(k\)进制下是\(m_0,m_1,m_2...\),则\(C_n^m≡C_{n_0}^{m_0}*C_{n_1}^{m_1}*C_{n_2}^{m_2}...\)
那么\(C_n^m\%k≡0\)等价于\(k\)进制下存在某一位\(C_{n_i}^{m_i}≡0\),即在k进制下n有一位小于m,这个组合数就为0所以做数位dp即可设\(dp[i][j][j][l]\)到当前i位有没有顶着n的上界,有没有顶着m的上界,之前是不是已经满足了\(n_i<m_i\)即组合数是否为0
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define se second
#define fi first
using namespace std;
const int N=1e6+10;
const int mod=1e9+7;
const ll inf1=0x3f3f3f3f3f3f3f3f;
const ll inf2=0xefefefefefefefef;
int maxx=1e6;
ll n,m,k,ans,cnt,rt;
ll bx[N],by[N];
ll jie[N],ni[N];
ll f[100][2][2][2][2];
ll read(){ll x;cin>>x;return x;}
struct zzz{
ll x,y,v;
}e[N];
int cmp(zzz a,zzz b){
return a.v<b.v;
}
ll ppow(ll n,ll m){ll s=1;while(m){if(m&1)s=s*n%mod;n=n*n%mod;m/=2;}return s;}
ll c(int n,int m){
if(n<m||n<0||m<0)
return 0;
return jie[n]*ni[m]%mod*ni[n-m]%mod;
}
ll ask(int len,bool f1,bool f2,bool f3,bool f4){
if(!len)return f1;
if(f[len][f1][f2][f3][f4]!=inf2){
return f[len][f1][f2][f3][f4];
}
int mai=f3?k-1:bx[len];
int maj=f4?k-1:by[len];
ll sum=0;
for(int i=0;i<=mai;i++){
for(int j=0;(j<=i||f2)&&j<=maj;j++){
sum+=ask(len-1,f1|(i<j),f2|(i!=j),f3|(i<bx[len]),f4|(j<by[len]));
sum%=mod;
}
}
return f[len][f1][f2][f3][f4]=sum;
}
int main(){
int T=read();
cin>>k;
while(T--){
ll x,y;
cin>>x>>y;
memset(f,0xef,sizeof f);
ll ma=max(x,y),len=0;
while(ma){
ma/=k;
len++;
}
for(int i=1;i<=len;i++){
bx[i]=x%k;
x/=k;
}
for(int i=1;i<=len;i++){
by[i]=y%k;
y/=k;
}
cout<<ask(len,0,0,0,0)<<endl;
}
}
「CodePlus 2018 4 月赛」组合数问题 2
题目描述
给你两个数 n和 k,希望你找到 k个不同的组合数使得这 k 个组合数的和最大。现在小葱希望你找到这样 k个不同的组合数,使得它们互不相同且对于其中任何一个组合数 \(C_a^b\) 有 0≤b≤a≤n.问这 k 个组合数的和最大是多少?对1e9+7取模
题解
求\(K\)个不同组合数的最大和。容易发现的是\(C_n^m\)都比要\(C_{n-1}^m\)都大。
于是一开始可以将所有的\(C_n^i\)就是杨辉三角最后一行的数都丢到一个优先队列中,每次取出最大值\(C_x^y\),然后将\(C_x^{y-1}再丢\)到优先队列中。重复\(k\)次即可。但是优先队列存不下这么大的数所以考虑对组合数取对数在放进优先队列里
\(log(C_x^y)=log({x!\over y!(x-y)!})=log(x!)-log(y!)-log((x-y)!)\)
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define se second
#define fi first
using namespace std;
const int N=1e6+10;
const int mod=1e9+7;
const ll inf=0x3f3f3f3f3f3f3f3f;
int maxx=1e6;
ll n,m,k,ans,cnt,rt;
ll a[N],b[N];
ll jie[N],ni[N];
double logg[N];
ll read(){ll x;cin>>x;return x;}
struct zzz{
ll x,y,v;
}e[N];
int cmp(zzz a,zzz b){
return a.v<b.v;
}
ll ppow(ll n,ll m){ll s=1;while(m){if(m&1)s=s*n%mod;n=n*n%mod;m/=2;}return s;}
ll c(int n,int m){
if(n<m||n<0||m<0)
return 0;
return jie[n]*ni[m]%mod*ni[n-m]%mod;
}
double logc(ll n,ll m){
return logg[n]-logg[m]-logg[n-m];
}
priority_queue<pair<double,pair<ll,ll>>> q;
int main(){
cin>>n>>m;
jie[0]=1;
for(int i=1;i<=maxx;i++)jie[i]=(jie[i-1]*i)%mod;
ni[maxx]=ppow(jie[maxx],mod-2);
for(int i=maxx;i;i--)ni[i-1]=(ni[i]*i)%mod;
logg[1]=log2(1);
for(int i=2;i<=maxx;i++){
logg[i]=(logg[i-1]+log2(i));
}
for(int i=0;i<=n;i++){
q.push({logc(n,i),{n,i}});
}
for(int i=1;i<=m;i++){
int x=q.top().se.fi,y=q.top().se.se;
q.pop();
q.push({logc(x-1,y),{x-1,y}});
(ans+=c(x,y))%=mod;
}
cout<<ans;
}
「SHOI2015」超能粒子炮・改
题意
有两个参数 n、k,它会向每个编号为 0 到 k(包含两端)的位置 i 发射威力为 $C_n^imod2333 $的粒子流。
给出了他的超能粒子炮・改的参数,让你求出其发射的粒子流的威力之和除以 2333 所得的余数。
即\(\sum_{i=0}^kC_n^i\)
题解
设$f(n,k)=,可以使用卢卡斯定理来修改
就是\(\sum_{i=0}^kC_{n\%p}^{i\%p}C_{n/p}^{i/p}\)
对着\(i/p\)相同的若干段分别计算
\(C_{n/p}^0*\sum_{i=0}^{p-1}C_{n\%p}^{i\%p}\)
\(+C_{n/p}^1*\sum_{i=p}^{2p-1}C_{n\%p}^{i\%p}\)
\(+C_{n/p}^2*\sum_{i=2p}^{3p-1}C_{n\%p}^{i\%p}\)
...
\(=\sum_{j=0}^{k/p-1}C_{n/p}^j*\sum_{i=0}^{p-1}C_{n\%p}^i\)
右边本来是在左边的\(\sum\)内部的,但是可以发现循环的是\(i\),和外面的\(j\)无关,直接拿到外面即可
因此\(i/p\)的前面一大堆可以替换为\(f(n/p,k/p-1)*f(n\%p,p-1)\)
对于最后一段不足\(p\)的一段:
\(\sum_{i=k/p*p}^kC_{n\%p}^{i\%p}C_{n/p}^{i/p}\)
=\(C_{n/p}^{k/p}\sum_{i=k/p*p}^kC_{n\%p}^{i\%p}\)
\(=C_{n/p}^{k/p}\sum_{i=0}^{k\%p}C_{n\%p}^i\)
\(=C_{n/p}^{k/p}f(n\%p,k\%p)\)
结论就是如果\(k\%p\)非\(0\),\(f(n,k)=f(n/p,k/p-1)*f(n\%p,p-1)+C_{n/p}^{k/p}f(n\%p,k\%p)\)
如果\(\%p=0\)不需要后面那块
如果\(f(n,k)\)落在了\(p\)内部,可以提前预处理掉,从而快速拿到答案。
\(C_{n/p}^{k/p}\)这块可以卢卡斯定理
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define se second
#define fi first
using namespace std;
const int N=3000+10;
const int mod=2333;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll maxx=N-10;
ll n,m,k,ans,cnt,t;
ll a[N],b[N],dp[N][N];
ll jie[N],ni[N];
ll read(){ll x;cin>>x;return x;}
struct zzz{
ll x,y,v;
}e[N];
int cmp(zzz a,zzz b){
return a.v<b.v;
}
ll ppow(ll n,ll m){ll s=1;while(m){if(m&1)s=s*n%mod;n=n*n%mod;m/=2;}return s;}
ll c(ll n,ll m){
if(n<m||n<0||m<0)
return 0;
return jie[n]*ni[m]%mod*ni[n-m]%mod;
}
ll lu(ll n,ll m){
if(n<m)return 0;
if(n==m)return 1;
if(!m)return 1;
return c(n%mod,m%mod)*lu(n/mod,m/mod)%mod;
}
ll f(ll n,ll m){
if(m<0)return 0;
if(!n||!m)return 1;
if(n<mod&&m<mod)return dp[n][m];
return ((f(n/mod,m/mod-1)*dp[n%mod][mod-1]%mod)+lu(n/mod,m/mod)*dp[n%mod][m%mod]%mod)%mod;
}
int main(){
jie[0]=1;
for(int i=1;i<mod;i++)jie[i]=(jie[i-1]*i)%mod;
ni[mod-1]=ppow(jie[mod-1],mod-2);
for(int i=mod-1;i;i--)ni[i-1]=(ni[i]*i)%mod;
ni[0]=1;
dp[0][0]=1;
for(int i=0;i<mod;i++){
dp[i][0]=1;
for(int j=1;j<mod;j++){
dp[i][j]=(dp[i][j-1]+c(i,j))%mod;
}
}
cin>>t;
while(t--){
ll x,y;
cin>>x>>y;
cout<<f(x,y)<<endl;
}
}

浙公网安备 33010602011771号