组合杂题
srO DeaphetS Orz
[HDU6333]Harvest of Apples
\(【题目描述】\)
有 \(T(T\leq 10^5)\) 次询问,每次询问给定两个整数 \(n,m(1\leq m\leq n\leq 10^5)\),求:\(\sum\limits_{i=0}^m\binom{n}{i}\) 的值。
\(【考点】\)
组合数、莫队
\(【做法】\)
多次询问区间且不带修,且答案具有连续性,一眼莫队,设 \(F(m,n)=\sum\limits_{i=0}^m \binom{n}{i}\),有:
又因为 \(m\leq n\) 才有贡献,因此直接把 \(m\) 当成 \(l\),\(n\) 当成 \(r\),用普通莫队维护即可。
\(【代码】\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+50;
const ll Inv2=500000004,mod=1e9+7;
ll FacInv[N],Inv[N],Fac[N],now=1,ans[N];
int n,len,L=1,R;
struct ques{
int l,r,id,num;
bool operator <(const ques b) const{
if(num==b.num) return num&1?r<b.r:r>b.r;
return num<b.num;
}
}q[N];
void init()
{
FacInv[1]=Inv[1]=Fac[1]=1;
FacInv[0]=Inv[0]=Fac[0]=1;
for(int i=2;i<N;i++){
Inv[i]=(mod-mod/i)*Inv[mod%i]%mod;
Fac[i]=Fac[i-1]*i%mod;
FacInv[i]=FacInv[i-1]*Inv[i]%mod;
}
}
ll C(int n,int m)
{
if(n<m||n<0) return 0;
return Fac[n]*FacInv[m]%mod*FacInv[n-m]%mod;
}
void updatel(int mm,int nn,ll v)
{
now=(now-v*C(nn,mm)+mod)%mod;
}
void updater(int mm,int nn,ll v)
{
if(v>0) now=(now*2-C(nn-1,mm)+mod)%mod;
else now=(now+C(nn-1,mm))%mod*Inv2%mod;
}
int main()
{
init();
scanf("%d",&n);
len=sqrt(n);
for(int i=1;i<=n;i++){
scanf("%d%d",&q[i].r,&q[i].l);
q[i].id=i,q[i].num=(q[i].l-1)/len+1;
}
sort(q+1,q+1+n);
for(int i=1;i<=n;i++){
while(L>q[i].l) updatel(L--,R,1);
while(R<q[i].r) updater(L,++R,1);
while(L<q[i].l) updatel(++L,R,-1);
while(R>q[i].r) updater(L,R--,-1);
ans[q[i].id]=now;
}
for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
return 0;
}
[HDU7060]Separated Number
\(【题目描述】\)
多组数据,每组数据给定一个整数 \(k\) 和一个长度为 \(n(\sum n\leq 10^6)\) 的数 \(x\),满足 \(k\leq n\),一种合法的方案被定义为将 \(x\) 分成不超过 \(k\) 个的整数,其贡献为拆分后的整数之和。问所有合法方案的贡献之和。
\(【考点】\)
组合数、dp
\(【做法】\)
直接枚举不现实,可以单独计算每个位置上的数对答案的贡献。对于从前往后数第 \(i\) 个数,每种合法方案中他对答案的贡献都形如 \(x_i\times 10^j\),其中 \(i+j\) 表示的是这个方案中他后面的第一条分割线所在的位置,由于可能插在最末尾,我们可以讨论 \(i+j\) 所在的位置:
- 若 \(i+j=n\),说明压根就没有分割线,因此需要在 \(i\) 前面的 \(n-j-1\) 个位置选择 \(k-1\) 个位置插入分割线,方案数为: \(\sum\limits_{i=0}^{k-1}\binom{i}{n-j-1}\)
- 若 \(i+j<n\),
说明有分割线,需要在除了 \(i\) 及末尾 \(n\) 的 \(n-j-2\) 个位置选择 \(k-2\) 个位置插入分割线,方案数为: \(\sum\limits_{i=0}^{k-2}\binom{i}{n-j-2}\)
那么最后的答案便是:
即:
然后后面那俩组合数的处理就像上面那到题目一样,令 \(F(n,m)=\sum\limits_{i=1}^m \binom{n}{i}\)
原式可化为:
有: \(F(n,m)=2F(n-1,m)-\binom{n-1}{m}\)
然后直接预处理俩 \(F\) 即可。
注意一下数组下标问题。
\(【代码】\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+50;
const ll mod=998244353;
ll Inv[N],FacInv[N],Fac[N],F[N][2],Pow[N],g;
//F[i][0]表示ΣC(j,k-1),F[i][1]表示ΣC(i,k-2)
int n,t,k;
char s[N];
void init()
{
Inv[1]=FacInv[1]=Fac[1]=1;
Inv[0]=FacInv[0]=Fac[0]=1;
for(int i=2;i<N;i++){
Inv[i]=(mod-mod/i)*Inv[mod%i]%mod;
FacInv[i]=FacInv[i-1]*Inv[i]%mod;
Fac[i]=Fac[i-1]*i%mod;
}
Pow[0]=1;
for(int i=1;i<N;i++) Pow[i]=Pow[i-1]*10%mod;
}
ll C(ll n,ll m)
{
if(n<m||n<0||m<0) return 0;
return Fac[n]*FacInv[n-m]%mod*FacInv[m]%mod;
}
ll solve()
{
F[0][0]=1;
if(k>1) F[0][1]=1;//注意这里要判k>1
for(int i=1;i<N;i++){
F[i][0]=(2*F[i-1][0]-C(i-1,k-1)+mod)%mod;
F[i][1]=(2*F[i-1][1]-C(i-1,k-2)+mod)%mod;
}
g=0;
for(int i=n-2;i>=0;i--){
if(n-i-2>=0) g=(g+Pow[i]*F[n-i-2][1]%mod)%mod;
}
ll ans=0;
for(int i=1;i<=n;i++){
ans=(ans+(s[i]-'0')*(Pow[n-i]*F[i-1][0]%mod+g))%mod;
if(i!=n) g=(g-Pow[n-i-1]*F[i-1][1]%mod+mod)%mod;
}
return ans;
}
int main()
{
init();
scanf("%d",&t);
while(t--){
scanf("%d",&k);
scanf("%s",s+1);
n=strlen(s+1);
printf("%lld\n",solve());
}
return 0;
}
[CF1716F]Bags with Balls
\(【题目描述】\)
共 \(T(T\leq 5000)\) 次询问,每次询问给出正整数 \(k,n,m(k\leq 2000,n,m\leq 998244352)\),设一个长度为 \(n\),元素取值在 \([1,m]\) 的数组 \(A\) 中的奇数元素个数为 \(x\),则有 \(F(A)=x^k\),对于所有合法 \(A\),求 \(\sum F(A)\mod 998244353\)
\(【考点】\)
组合数、第二类斯特林数、导数
\(【做法】\)
通过枚举奇数个数将柿子列出:
令 \(x=\lfloor \frac{m+1}{2} \rfloor , y=\lfloor \frac{m}{2} \rfloor\)
几项分别为:贡献、枚举奇数位置、枚举奇数取值、枚举偶数取值
接下来可以通过组合恒等式 \(\sum\limits_{i=1}^n i\cdot\binom{n}{i}=n\cdot 2^{n-1}\) 和 \(\sum\limits_{i=1}^n i^2\cdot \binom{n}{i}=n(n+1)\times 2^{n-2}\) 进行函数求导。
另一种做法是暴力推柿子,因为有:
可以搞掉这个 \(i^k\):
然后 \(O(k^2)\) 预处理第二类斯特林数、动态维护下降幂即可。
\(【代码】\)
#include<bits/stdc++.h>
#define ine long long
using namespace std;
typedef long long ll;
const int N=2e3+50;
const ll mod=998244353;
ll s2[N][N],ans;
ll n,m,k,t;
ll Pow(ll a,ll b)
{
ll ans=1,base=a;
while(b){
if(b&1) ans=ans*base%mod;
base=base*base%mod;
b>>=1;
}
return ans;
}
ll solve()
{
scanf("%lld%lld%lld",&n,&m,&k);
ll x=m-m/2,now=n*Pow(m,n-1)%mod*x%mod,invm=Pow(m,mod-2);
ans=0;
for(int j=1;j<=min(n,k);j++){
ans=(ans+s2[k][j]*now%mod)%mod;
now=now*(n-j)%mod*invm%mod*x%mod;
}
return ans;
}
void init()
{
s2[1][1]=1;
for(int i=2;i<N;i++){
for(int j=1;j<=i;j++) s2[i][j]=(s2[i-1][j-1]+j*s2[i-1][j]%mod)%mod;
}
}
signed main()
{
init();
scanf("%lld",&t);
while(t--){
printf("%lld\n",solve());
}
return 0;
}
[ARC137D]Prefix XORs
\(【题目描述】\)
给定序列长度为 \(n(n\leq 10^6)\) 的序列 \(A\) 以及整数 \(m(m\leq 10^6)\),定义一次操作为:对每个 \(i\leq n\),令 \(B_i=\bigoplus\limits_{j=1}^i A_j\),然后令 \(A=B\) 。对于每个 \(k=1,2,...,m\),求出经过 \(k\) 次操作后 \(A_n\) 的值。
\(【考点】\)
组合数、Lucas定理、FWT
\(【做法】\)
为方便,可以定义一个 \(A_{i,j}\) 表示经过了 \(i\) 次操作后 \(A_j\) 的值,一开始 \(A\) 就是 \(A_{0,i}\),然后根据异或的前缀性质,有: \(A_{i,j}=A_{i,j-1}\oplus A_{i-1,j}\),简化了操作过程。
然后考虑每一个 \(A_{0,i}\) 对 \(A_{k,n}\) 产生的贡献,稍微列一下可以发现为 \(\binom{n+k-i}{k}\),同时由于是异或运算,只需要考虑其奇偶性,利用Lucas定理的推论: \(\binom{n}{m}\) 为奇数当且仅当二进制下 \(m\) 为 \(n\) 的子集。也就是说只有满足 \(k \in n+k-i\),即 \(k\cap n-i=\emptyset\),的 \(A_{0,i}\) 才对 \(A_{k,n}\) 有贡献
这样就把问题转化成了:对于所有 \(k\),统计出 \(\bigoplus\limits_{k\in n+k-i} A_{0,i}\) ,用FWT即可。
\(【代码】\)
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<20);
int a[N],n,m,ans;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[n-i]);
for(int j=0;j<=20;j++){
for(int i=0;i<N;i++){
if(i&(1<<j)) a[i]^=a[i-(1<<j)];
}
}
for(int i=1;i<=m;i++) printf("%d ",a[N-i]);
return 0;
}
[CF1713F]Lost Array
\(【题目描述】\)
对于一个长度为 \(n(n\leq 5\times 10^5)\) 的序列 \(A\),对于每个 \(k=1,2,...,n\),定义一次操作为:对每个 \(i\leq n\),令 \(B_{k,i}=\bigoplus\limits_{j=1}^i A_j\),然后令 \(A=B_k\)。现给定 \(B_n\),求原序列 \(A\)
\(【考点】\)
咕咕咕咕
\(【做法】\)
咕咕咕咕
\(【代码】\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+50;
int a[N],n;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int j=0;j<19;j++){
for(int i=0;i<n;i++){
if(i&(1<<j)) a[i]^=a[i^(1<<j)];
}
}
for(int j=0;j<19;j++){
for(int i=0;i<n;i++){
if(i&(1<<j)) a[i^(1<<j)]^=a[i];
}
}
for(int i=n-1;i>=0;i--) printf("%d ",a[i]);
return 0;
}
无题
\(【题目描述】\)
给定正整数 \(n(n\leq 5\times 10^5)\),对所有 \(k\in[1,2n]\),求出有多少个长度为 \(2n\) 的合法括号序列满足这个序列的第 \(k\) 位是左括号。
\(【考点】\)
卡特兰数
\(【做法】\)
考虑第 \(k\) 位与右边的哪一位匹配,将序列转化成 \(A(B)C\) 的形式,而其中 \(B\) 与 \(AC\) 均为合法括号序列。于是:
其中 \(i\) 表示的是 \(B\) 的长度,\(h_i\) 表示长度为 \(2i\) 的合法括号序列方案,即卡特兰数。 \(O(n)\) 预处理卡特兰数即可。

浙公网安备 33010602011771号