组合杂题

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}\),有:

\[F(n,m)=F(n,m-1)+\binom{n}{m} \]

\[F(n,m)=2F(n-1,m)-\binom{n-1}{m} \]

又因为 \(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\) 所在的位置:

  1. \(i+j=n\),说明压根就没有分割线,因此需要在 \(i\) 前面的 \(n-j-1\) 个位置选择 \(k-1\) 个位置插入分割线,方案数为: \(\sum\limits_{i=0}^{k-1}\binom{i}{n-j-1}\)
  2. \(i+j<n\)说明有分割线,需要在除了 \(i\) 及末尾 \(n\)\(n-j-2\) 个位置选择 \(k-2\) 个位置插入分割线,方案数为: \(\sum\limits_{i=0}^{k-2}\binom{i}{n-j-2}\)

那么最后的答案便是:

\[ans=\sum\limits_{i=1}^n a_i\times \left( \sum\limits_{i+j=n}10^j\times \sum\limits_{l=0}^{k-1}\binom{l}{n-j-1}+\sum\limits_{i+j<n}10^j\times \sum\limits_{l=0}^{k-2}\binom{l}{n-j-2}\right) \]

即:

\[ans=\sum\limits_{i=1}^n a_i\times \left( 10^{n-i}\times \sum\limits_{l=0}^{k-1}\binom{l}{i-1}+\sum\limits_{j=0}^{n-i-1}10^j\times \sum\limits_{l=0}^{k-2}\binom{l}{n-j-2}\right) \]

然后后面那俩组合数的处理就像上面那到题目一样,令 \(F(n,m)=\sum\limits_{i=1}^m \binom{n}{i}\)

原式可化为:

\[ans=\sum\limits_{i=1}^n a_i\times \left(10^{n-i}\times F(i-1,k-1)+\sum\limits_{j=0}^{n-i-1}10^j\times F(n-j-2,k-2)\right) \]

有: \(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\)

\[ans=\sum\limits_{i=1}^n i^k \cdot \binom{n}{i}\cdot x^{i}\cdot y^{n-i} \]

几项分别为:贡献、枚举奇数位置、枚举奇数取值、枚举偶数取值

接下来可以通过组合恒等式 \(\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=\sum\limits_{l=0}^k \begin{Bmatrix}k \\ l \end{Bmatrix}\binom{i}{l}j! \]

可以搞掉这个 \(i^k\)

\[\begin{align} ans&=\sum\limits_{i=1}^n \binom{n}{i} x^i y^{n-i}\cdot \sum\limits_{j=0}^k \begin{Bmatrix} k \\ j \end{Bmatrix} \binom{i}{j}j! \\ &= \sum\limits_{j=0}^k \begin{Bmatrix} k \\ j \end{Bmatrix} \sum\limits_{i=1}^n \frac{n!}{(n-i)!(i-j)!}x^iy^{n-i} \\ &= \sum\limits_{j=0}^k \begin{Bmatrix} k \\ j \end{Bmatrix} \cdot \frac{n!}{(n-j)!} \sum\limits_{i=1}^n \frac{(n-j)!}{(n-i)!(i-j)!} x^i y^{n-i} \\ &= \sum\limits_{j=0}^k \begin{Bmatrix} k \\ j \end{Bmatrix} \cdot n^{\underline j}\sum\limits_{i=1}^n \binom{n-j}{n-i} x^i y^{n-i} \\ &=\sum\limits_{j=0}^k \begin{Bmatrix} k \\ j \end{Bmatrix} \cdot n^{\underline j}\sum\limits_{l=0}^{n-j}\binom{n-j}{l}x^{j}\cdot x^{n-j-t}y^t \\ &=\sum\limits_{j=0}^k\begin{Bmatrix} k \\ j \end{Bmatrix} \cdot n^{\underline j} \cdot x^j\cdot(x+y)^{n-j} \\ &=\sum\limits_{j=0}^k\begin{Bmatrix} k \\ j \end{Bmatrix} \cdot n^{\underline j} \cdot x^j\cdot m^{n-j} \end{align} \]

然后 \(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\) 均为合法括号序列。于是:

\[1a1ns=\sum\limits_{i=0}^{\frac{2n-k-1}{2}}h_i\cdot h_{n-i-1} \]

其中 \(i\) 表示的是 \(B\) 的长度,\(h_i\) 表示长度为 \(2i\) 的合法括号序列方案,即卡特兰数。 \(O(n)\) 预处理卡特兰数即可。

posted @ 2022-11-07 17:16  lxzy  阅读(35)  评论(0)    收藏  举报