数学专练(北京集训)

A. [ARC147D] Sets Scores

较为简单的(因为除他以外,非黑即紫)。

考虑我们可以显然可以用一个长为 \(m\)\(bool\) 数组 \(c\) 和一个长为 \(n-1\)\(int\) 数组 \(p\) 唯一表示一种情况。考虑用 \(c\) 数组表示每个位置初始在不在 \(S_1\) 里,\(p\) 表示每次改变了哪个元素的状态。

考虑固定 \(p\)。发现每个位置 \(c\) 都有 \(0,1\) 两种取值,且两种情况下,该元素出现次数之和恰好为 \(n\),所以此时总和为 \(n^m\)。再乘上 \(p\) 的种类数 \(m^{n-1}\),答案即为 \(n^m\times m^{n-1}\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,p=998244353;
int n,m;
inline int qpow(int x,int y){
	int re=1;
	while(y){
		if(y&1) re=1ll*re*x%p;
		x=1ll*x*x%p,y>>=1;
	}
	return re;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	cout<<1ll*qpow(n,m)*qpow(m,n-1)%p;
	return 0;
}

B. [CERC2015] Frightful Formula

容易想到组合意义,考虑从每个位置出发,对 \(F_{n,n}\) 的贡献是多少,则有:

\[F_{n,n}=\sum_{i=2}^n l_ia^{n-1}b^{n-i}\binom{2n-i-2}{n-i}+\sum_{i=2}^n t_ia^{n-i}b^{n-1}\binom{2n-i-2}{n-i}+\sum_{i=0}^{n-2}\sum_{j=0}^{n-2}\binom{i+j}ia^ib^j \]

前两个 \(\sum\) 是好算的,关键是第三个。显然可以用三模数 \(ntt\) 直接艹过去,但是太不文明了。考虑拆成两部分:

  1. \(i+j\le n-2\)。此时即为杨辉三角:
    \(ans=\sum_{i=0}^{n-2}\sum_{j=0}^i\binom ija^jb^{i-j}=\sum_{i=0}^{n-2}(a+b)^i\)
  2. \(i+j>n-2\)。考虑组合意义。每一次我们相当于从原先的位置分别向横纵向各走一步,所以总体乘上 \(a+b\)。但是每一次两端是不能走的,所以要减去两种情况。设 \(f_i=\sum_{j=i-n+2}^{n-2}\binom ija^jb^{i-j}(i>n-2)\),则有递推式:
    \(f_i=(a+b)f_{i-1}-\binom{i-1}{n-2}(a^{n-1}b^{i-n+1}+a^{i-n+1}b^{n-1})\)

这样就可以 \(O(n)\) 解决了。

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5,p=1e6+3;
int n,a,b,c,ac[N],bc[N],ans,jc[N],inv[N],f[N];
inline int qpow(int x,int y){
	int re=1;
	while(y){
		if(y&1) re=1ll*re*x%p;
		x=1ll*x*x%p,y>>=1;
	}
	return re;
}
inline int C(int x,int y){
	return 1ll*jc[x]*inv[y]%p*inv[x-y]%p;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>a>>b>>c;
	for(int i=1;i<=n;i++) cin>>ac[i];
	for(int i=1;i<=n;i++) cin>>bc[i];
	jc[0]=inv[0]=1;
	for(int i=1;i<=n*2;i++) jc[i]=1ll*jc[i-1]*i%p;
	inv[n*2]=qpow(jc[n*2],p-2);
	for(int i=n*2-1;i;i--) inv[i]=(i+1ll)*inv[i+1]%p;
	for(int i=0;i<=n-2;i++)
		f[n-2]=(f[n-2]+1ll*C(n-2,i)*qpow(b,i)%p*qpow(a,n-i-2))%p;
	for(int i=n-1;i<=2*n-4;i++)
		f[i]=((1ll*f[i-1]*(a+b)%p-1ll*C(i-1,i-n+1)*qpow(b,i-n+1)%p*qpow(a,n-1)-1ll*C(i-1,n-2)*qpow(b,n-1)%p*qpow(a,i-n+1))%p+p)%p,ans=(ans+f[i])%p;
	for(int i=0;i<=n-2;i++) ans=(ans+qpow(a+b,i))%p;
	ans=1ll*ans*c%p;
	for(int i=2;i<=n;i++)
		ans=(ans+1ll*ac[i]*qpow(b,n-i)%p*qpow(a,n-1)%p*C(n*2-i-2,n-i))%p;
	for(int i=2;i<=n;i++)
		ans=(ans+1ll*bc[i]*qpow(b,n-1)%p*qpow(a,n-i)%p*C(n*2-i-2,n-i))%p;
	cout<<ans;
	return 0;
}

C. [ARC139D] Priority Queue 2

\(c(A)\) 表示在数组中满足 \(A\) 性质的数的数量。

考虑 \(\sum i\times c(=i)=\sum c(\ge i)\),问题转化为求解所有的 \(c(\ge i)\)

设加入的数为 \(d\),考虑什么时候 \(c(\ge i)\) 会变化:

  1. \(c(\ge i)>n-x+1\)。此时一个 \(\ge i\) 的数会被删掉。
  2. \(d\ge i\)。显然加一。

第一部分我们可以延迟操作,因此,我们就可以通过枚举最终 \(c(\ge i)\) 的方式对答案进行简单的计算。
我们可以枚举 \(c(\ge i)\) 在和 \(n-x+1\)\(\min\) 之前的增量 \(j\),设 \(c'_i\) 表示 \(a\) 数组中比 \(i\) 大的数的数量,则有:

\[ans=\sum_{i=1}^m\sum_{j=0}^k(m-i+1)^j(i-1)^{k-j}\binom kj\max(\min(c'_i+j,n-x+1),c'_i+j-k) \]

时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
const int N=2005,p=998244353;
int n,m,k,x,a,c[N],C[N][N],ans;
inline int qpow(int x,int y){
	int re=1;
	while(y){
		if(y&1) re=1ll*re*x%p;
		x=1ll*x*x%p,y>>=1;
	}
	return re;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>k>>x;
	for(int i=1,a;i<=n;i++){
		cin>>a;
		c[a]++;
	}
	for(int i=m;i;i--) c[i]+=c[i+1];
	for(int i=0;i<=k;i++){
		C[i][0]=1;
		for(int j=1;j<=i;j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
	}
	for(int i=1;i<=m;i++) for(int j=0;j<=k;j++)
		ans=(ans+1ll*qpow(m-i+1,j)*qpow(i-1,k-j)%p*C[k][j]%p*max(c[i]+j-k,min(j+c[i],n-x+1)))%p;
	cout<<ans;
	return 0;
}

D. [CF1097G] Vladislav and a Great Legend

感觉评不上黑。

考虑大力组合意义,这样就可以将问题转化为求选出一棵虚树后,在虚树上选出 \(i\) 条边的方案数。容易想到一个叫做树上背包的东西。

\(dp_{i,j}\) 表示在以 \(i\) 为根的子树内建立的非空虚树(含根节点和根节点到父亲边)中选 \(j\) 条边的方案数,则有:

\[dp_{u,i+j}=dp_{u,i}dp_{v,j} \]

\[dp_{u,i-1}\to dp_{u,i} \]

由于要求非空,所以最后每次还得给 \(dp_{u,1}-1\)(只有根节点到父亲边);由于要统计答案,所以每次在算完第一个式子之后就对应加上,同时为了刨去根节点除了虚树以外除了根节点,每次结束后还要额外减一次。

时间复杂度 \(O(nk)\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=205,p=1e9+7;
int n,k,s[M][M],dp[N][M],tmp[M],sz[N],ans[M],as;
vector<int>g[N];
inline void dfs(int x,int fa){
	sz[x]=1,dp[x][0]=2;
	for(int y:g[x]) if(y!=fa){
		dfs(y,x);
		for(int i=0;i<=min(sz[x],k);i++)
			for(int j=0;j<=min(sz[y],k);j++)
				tmp[i+j]=(tmp[i+j]+1ll*dp[x][i]*dp[y][j])%p;
		sz[x]+=sz[y];
		for(int i=0;i<=min(sz[x],k);i++)
			dp[x][i]=tmp[i],ans[i]=(ans[i]-dp[y][i]+p)%p,tmp[i]=0;
	}
	for(int i=0;i<=k;i++) ans[i]=(ans[i]+dp[x][i])%p;
	for(int i=k;i;i--) dp[x][i]=(dp[x][i]+dp[x][i-1])%p;
	dp[x][1]=(dp[x][1]+p-1)%p;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>k;
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1,0),s[0][0]=1;
	for(int i=1;i<=k;i++) for(int j=1;j<=i;j++)
		s[i][j]=(s[i-1][j-1]+1ll*j*s[i-1][j])%p;
	for(int i=0,jc=1;i<=k;i++,jc=1ll*jc*i%p)
		as=(as+1ll*s[k][i]*jc%p*ans[i])%p;
	cout<<as;
	return 0;
}
posted @ 2025-12-14 20:32  white_tiger  阅读(17)  评论(0)    收藏  举报