MX 暑假集训 8.1

ゲンチアナ(Gentiana)- LonePi feat.花隈千冬

いたいのいたいのとんでいけ
痛苦啊痛苦飞走吧

真っ白真綿で抱き占めて
化作纯白的锦缎拥抱你

龙胆花真的很好听!LonePi 我们喜欢你!

组合计数专题。难。

Research Rover

题意

给定一个 \(n\times m\) 的网格图,给定 \(k\) 个位置,这些位置上是黑色网格,其余位置是白色网格,一辆车从 \((1,1)\) 移动到 \((n,m)\),移动过程中 \(x,y\) 坐标都不减,初始有一个电量 \(s\),若碰到一个黑色网格,若当前电量为 \(x\),则电量变成 $\left\lceil \frac{x}{2}\right\rceil $,碰到白色格子电量不变,求最后电量的期望值。

\(1\le n,m,s\le 10^5,0\le k\le 2000\)

solution

\(k\) 很小,所以考虑对黑色格子 dp,令 \(f_{i,j}\) 表示走到第 \(i\) 个黑色格子,共经过了 \(j\) 个黑色格子的方案数,令 \(p(i,k)\) 为从第 \(i\) 个黑色格子走到第 \(k\) 个黑色格子的方案数,令 \(g(i,k)\) 为从第 \(i\) 个黑色格子走到第 \(k\) 个黑色格子,且中途不碰到任何黑色格子的方案数,那么有转移 \(f_{i,j}\leftarrow f_{k,j-1}g(k,i)\)

我们发现 \(g(i,k)\) 是很难求出的,考虑求出从 \(i\) 走到 \(k\) 经过了至少一个黑色格子的方案数,再用所有方案减去得到合法方案数。

对于一组点 \(A,B,C\),要处理 \(A\)\(C\) 的转移,\(B\) 为第一次经过的黑色格子,那么对于 \(A\)\(B\) 不经过任何黑色格子的方案数在计算 \(f_B\) 时已经被计入,所以可以得到转移式子 \(f_{i,j}\leftarrow (f_{k,j-1}-f_{k,j})p(k,i)\),其中 \(f_{k,j-1}\) 的意义是 \(k\)\(i\) 转移的总方案数,\(f_{k,j}\) 的意义是,减去所有前面的点 \(f_{A,j-1}\) 经过了点 \(k\) 的所有路径数。\(p(k,i)\) 可以预处理组合数 \(O(1)\) 求出。

这样的转移是 \(O(k^3)\) 的,但是发现最多经过 \(\log s\) 个黑色格子电量就会变成 \(1\),所以只需要计算经过黑色格子个数不超过 \(\log s\) 的部分,将这部分的贡献计入答案,再用所有方案数减去这部分的所有方案数即可得到经过超过 \(\log s\) 个黑色格子的方案数,这部分电量一定为 \(1\),直接计入贡献即可。

时间复杂度 \(O(n+m+k^2\log s)\),实现时可以钦定 \((1,1)\)\((n,m)\) 也是黑色格子,最后得到黑色格子数量减去即可。

Code
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)
constexpr int N=2e5+10;
constexpr int inf=1e9+10;

constexpr int mod=1e9+7;
inline int ad(int x,int y) {return ((x+y>=mod)?(x+y-mod):(x+y));}
inline void add(int &x,int y) {x=ad(x,y);}

inline ll qpow(ll a,int b) {
	ll res=1;
	while (b) {
		if (b&1) res=res*a%mod;
		a=a*a%mod;b>>=1;
	}
	return res;
}

inline void read(int &x) {
	x=0;
	char ch=getchar();
	while (ch<48) ch=getchar(); 
	while (ch>=48) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}

ll fac[N],inv[N];
inline void init(int n) {
	fac[0]=inv[0]=1;
	f(i,1,n) fac[i]=fac[i-1]*i%mod;
	inv[n]=qpow(fac[n],mod-2);
	F(i,n-1,1) inv[i]=inv[i+1]*(i+1)%mod;
}

inline ll C(int n,int m) {
	if (n<0||m<0||n<m) return 0;
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int n,m,k,s;
int dp[N][23];

struct Node{int x,y;}p[N];
inline bool cmp(Node a,Node b) {return (a.x==b.x)?(a.y<b.y):(a.x<b.x);}
inline int calc(Node a,Node b) {return C(b.x-a.x+b.y-a.y,b.x-a.x);}

int main() {
	read(n);read(m);read(k);read(s);init(N-1);
	f(i,1,k) read(p[i].x),read(p[i].y);
	p[++k]={1,1};p[++k]={n,m};
	sort(p+1,p+1+k,cmp);
	dp[1][0]=1;
	f(i,2,k) f(j,1,i-1) f(d,1,20) add(dp[i][d],1ll*ad(dp[j][d-1],mod-dp[j][d])*calc(p[j],p[i])%mod);
	ll sum=0,ans=0,ps=calc(p[1],p[k]);
	f(i,1,20) {
		ans+=1ll*s*dp[k][i]%mod;
		sum+=dp[k][i];
		s=(s+1)/2;
	}
	ans+=ad(ps,mod-sum%mod);
	printf("%d\n",ans%mod*qpow(ps,mod-2)%mod);
	return 0;
}

[省选联考 2020 A 卷] 组合数问题

题意

给定 \(n,x,p,m\) 和一个 \(m\) 次多项式 \(f(k)\),求:

\[\left(\sum_{k=0}^{n}f(k)\times x^k\times \binom{n}{k}\right)\bmod p \]

\(1\le n, x, p \le 10^9, 0\le a_i\le 10^9, 0\le m \le \min(n,1000)\)

solution

呃呃推式子题。

给出几个关键的式子:

\[\binom{n}{k}\times k^{\underline m}= \binom{n-m}{k-m}\times n^{\underline m} \]

\[k^i= \sum_{j=0}^{i} {i\brace j} k^{\underline j} \]

\[(1+x)^n=\sum_{i=0}^{n}\binom{n}{i} x^i \]

原式可以写成:

\[\sum_{k=0}^n \sum_{i=0}^m a_i x^k k^i \binom{n}{k} \]

由普通幂转下降阶乘幂得:

\[\sum_{k=0}^n \sum_{i=0}^m a_i x^k \sum_{j=0}^i {i\brace j} k^{\underline j} \binom{n}{k} \]

再将下降阶乘幂和组合数转化一下:

\[\sum_{k=0}^n \sum_{i=0}^m a_i x^k \sum_{j=0}^i {i\brace j} n^{\underline j} \binom{n-j}{k-j} \]

交换一下求和顺序,并把只与 \(i,j\) 有关的项放到前面:

\[\sum_{i=0}^m \sum_{j=0}^i a_i {i\brace j} n^{\underline j} \sum_{k=0}^n x^k \binom{n-j}{k-j} \]

\(i,j\) 是可以枚举的,所以我们要化简这个式子:

\[\sum_{k=0}^n x^k \binom{n-j}{k-j} \]

\(t=k-j\)\(t<0\) 的项均为 \(0\) 得:

\[\sum_{t=0}^{n-j} x^{t+j} \binom{n-j}{t} \]

\(x^j\) 提到前面计算:

\[\sum_{k=0}^{n-j} x^{t} \binom{n-j}{t} \]

这就是一个喜闻乐见的二项式定理了,等于:

\[(x+1)^{n-j} \]

整理一下,最后的式子是:

\[\sum_{i=0}^m \sum_{j=0}^i a_i {i\brace j} n^{\underline j} x^j (x+1)^{n-j} \]

暴力递推第二类斯特林数,暴力枚举 \(i,j\) 即可做到 \(O(m^2)\)

Code
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)
constexpr int N=1000+10;
constexpr int inf=1e9+10;

inline void read(int &x) {
	x=0;
	char ch=getchar();
	while (ch<48) ch=getchar(); 
	while (ch>=48) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}

int mod;
inline int ad(int x,int y) {return (x+y>=mod)?(x+y-mod):(x+y);}
inline void add(int &x,int y) {x=ad(x,y);}

inline ll qpow(ll a,int b) {
	ll res=1;
	while (b) {
		if (b&1) res=res*a%mod;
		a=a*a%mod;b>>=1;
	}
	return res;
}

int n,x,m;
int a[N],S[N][N],dn[N];

int main() {
	read(n);read(x);read(mod);read(m);
	f(i,0,m) read(a[i]);
	ll ans=0;
	S[0][0]=1;dn[0]=1;
	f(j,0,m) {
		if (j) dn[j]=1ll*dn[j-1]*(n-j+1)%mod;
		ll px=qpow(x+1,n-j)*qpow(x,j)%mod*dn[j]%mod;
		f(i,j,m) {
			if (i&&j) add(S[i][j]=S[i-1][j-1],1ll*j*S[i-1][j]%mod);
			ans+=px*a[i]%mod*S[i][j]%mod;
		}
	}
	printf("%lld\n",ans%mod);
	return 0;
}

[HNOI2011] 卡农

题意

\(n\) 个音阶,由若干个音阶构成的非空集合称为片段,求有多少种本质不同的大小为 \(m\) 的片段的集合,满足所有音阶出现次数均为偶数。

\(1\le n,m\le 10^6\)

solution

自己根本不会推。

考虑转化题意,将一个片段写成一个 \(n\) 维向量(也就是二进制数),令全集 \(S={1,2,\dots ,2^n-1}\),一个合法片段对应了集合中的一个元素(对于每个音阶,选中则对应位为 \(1\),否则为 \(0\)),那么答案转化为求 \(\sum\limits_{u\subseteq S}[\left\lvert u\right\rvert =m][\bigoplus\limits_{v\in u} v=0]\),其中 $\oplus $ 表示异或操作。

考虑递推,令 \(f_i\) 表示从 \(S\) 中选出了 \(i\) 个不同的数,满足异或和为 \(0\) 的方案数,为了方便我们先钦定这个集合是有序的,也就是选出了一个序列,由于确定了前 \(i-1\) 个数,由异或和为 \(0\) 就能唯一确定最后一个数,但是这样会有不合法的情况,考虑不合法的情况有哪些:

  • \(i-1\) 个数异或和为 \(0\),方案数为 \(f_{i-1}\)
  • \(i-1\) 个数的异或和为前 \(i-1\) 个数中的一个,那么除了这个数其他数的异或和为 \(0\),这个数可以在前 \(i-1\) 个位置中的任意一个,且有 \(\left\lvert S\right\rvert-(i-2)\) 种取值,所以方案数为 \(f_{i-2}\times (i-1)\times (\left\lvert S\right\rvert-i+2)\)

这两种情况包含所有不合法情况且相互不重,所以用总方案数 \(A_{\left\lvert S\right\rvert}^{i-1}\) 减去这两种情况的方案数即可,即:

\[f_i=A_{\left\lvert S\right\rvert}^{i-1} - f_{i-1} - f_{i-2}\times (i-1)\times (\left\lvert S\right\rvert-i+2) \]

边界状态 \(f_0=1,f_1=0\),最后要乘上 \(\frac{1}{m!}\) 去掉有序限制,时间复杂度 \(O(m)\)

Code
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)
constexpr int N=1e6+10;
constexpr int mod=1e8+7;
inline int ad(int x,int y) {return ((x+y>=mod)?(x+y-mod):(x+y));}
inline void add(int &x,int y) {x=ad(x,y);}

inline ll qpow(ll a,int b) {
	ll res=1;
	while (b) {
		if (b&1) res=res*a%mod;
		a=a*a%mod;b>>=1;
	}
	return res;
}

int main() {
	int n,m;scanf("%d%d",&n,&m);n=qpow(2,n)-1;
	static int dp[N],A[N];dp[0]=A[0]=1;
	f(i,2,m) add(dp[i]=(A[i-1]=1ll*A[i-2]*(n-i+2)%mod),ad(mod-dp[i-1],mod-(1ll*dp[i-2]*(i-1)%mod*(n-i+2)%mod)));
	ll fc=1;f(i,2,m) fc=fc*i%mod;
	printf("%d\n",dp[m]*qpow(fc,mod-2)%mod);
	return 0;
}
posted @ 2026-08-01 19:18  Ff472130  阅读(9)  评论(0)    收藏  举报