【比赛记录】2025CSP-S模拟赛59
A | B | C | D | Sum | Rank |
---|---|---|---|---|---|
- | - | 11 | 0 | 11 | 21/25 |
A. 一个赢家
显然总方案数为 \(\frac{(2n)!}{2^n}\),考虑求出合法方案数。
又显然最大值 \(i\in[2n+1,4n-1]\)。又又显然 \(i\) 的组成方式为 \(x=\lceil\frac{4n-i}{2}\rceil\)。考虑钦定一个组成 \(a+b(a>b)\),于是对于另一种组合 \(a'+b'\),要求实际给出的组合 \(a'+c\) 满足 \(c<b'\)。从大到小考虑每个 \(a'\),发现每个 \(c\) 的取值都为 \(i-2n-1\)。于是最大值为 \(i\) 的方案数即为:
\[\frac{nx{n-1\choose x-1}(x-1)!(i-2n-1)^{x-1}[2(n-x)]!}{2^{n-x}}
\]
时间复杂度 \(O(n\log n)\)。轻微卡常,卡卡即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e7+5,mod=1e9+7;
il int qpow(int x,int y=mod-2){
int res=1;
while(y){
if(y&1){
res=res*1ll*x%mod;
}
x=x*1ll*x%mod,y>>=1;
}
return res;
}
int n,fac[maxn],inv[maxn],_2[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
if(n==1){
cout<<1;
return 0;
}
fac[0]=1;
for(int i=1;i<=n<<1;i++){
fac[i]=fac[i-1]*1ll*i%mod;
}
inv[n<<1]=qpow(fac[n<<1]);
for(int i=n<<1;i;i--){
inv[i-1]=inv[i]*1ll*i%mod;
}
_2[0]=1;
for(int i=1;i<=n;i++){
_2[i]=_2[i-1]*500000004ll%mod;
}
int ans=0;
for(int i=2*n+1;i<n<<2;i++){
int x=(4*n-i+1)>>1;
ans=(x*1ll*n%mod*fac[n-1]%mod*inv[n-x]%mod*qpow(i-2*n-1,x-1)%mod*fac[(n-x)<<1]%mod*_2[n-x]+ans)%mod;
}
cout<<ans*1ll*qpow(2,n)%mod*inv[n<<1]%mod;
return 0;
}
}
int main(){return asbt::main();}
B. 磁铁
考虑对于磁铁的一个排列 \(p\),求出使磁铁顺次摆放能占据的最小的空间 \(f(p)\),于是会对答案造成 \({l-f(p)+n\choose n}\) 的贡献。考虑求出 \(g(x)\) 表示 \(f(p)=x\) 的 \(p\) 的数量。
发现对于两个磁铁 \(r_i\) 和 \(r_j\),影响距离的是 \(\max(r_i,r_j)\),于是将 \(r\) 排序后进行连续段 DP。设 \(dp_{i,j,k}\) 表示前 \(i\) 个磁铁形成了 \(j\) 段,占据的总空间为 \(k\) 的方案数,于是又转移:
\[dp_{i,j,k}\times(j+1)\to dp_{i+1,j+1,k+1}\\
dp_{i,j,k}\times2\times j\to dp_{i+1,j,k+r_{i+1}}\\
dp_{i,j,k}\times(j-1)\to dp_{i+1,j-1,k+2r_{i+1}-1}
\]
于是 \(g(x)=dp_{n,1,x}\)。时间复杂度 \(O(n^2l)\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=3e4+5,mod=1e9+7;
int n,m,a[55],fac[maxn],inv[maxn];
int f[55][55][maxn];
il int qpow(int x,int y=mod-2){
int res=1;
while(y){
if(y&1){
res=res*1ll*x%mod;
}
x=x*1ll*x%mod,y>>=1;
}
return res;
}
il void init(){
fac[0]=1;
for(int i=1;i<=m;i++){
fac[i]=fac[i-1]*1ll*i%mod;
}
inv[m]=qpow(fac[m]);
for(int i=m;i;i--){
inv[i-1]=inv[i]*1ll*i%mod;
}
}
il int C(int x,int y){
if(x<y||y<0){
return 0;
}
return fac[x]*1ll*inv[y]%mod*inv[x-y]%mod;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
init();
f[0][0][0]=1;
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
for(int k=0;k<=m;k++){
f[i+1][j][k+a[i+1]]=(f[i][j][k]*2ll*j+f[i+1][j][k+a[i+1]])%mod;
f[i+1][j+1][k+1]=(f[i][j][k]*1ll*(j+1)+f[i+1][j+1][k+1])%mod;
if(j){
f[i+1][j-1][k+2*a[i+1]-1]=(f[i][j][k]*1ll*(j-1)+f[i+1][j-1][k+2*a[i+1]-1])%mod;
}
}
}
}
int ans=0;
for(int i=0;i<=m;i++){
ans=(C(m-i+n,n)*1ll*f[n][1][i]+ans)%mod;
}
cout<<ans;
return 0;
}
}
int main(){return asbt::main();}
C. 树上染黑
定义 \(f(u,A)\) 表示 \(u\) 到 \(A\) 的虚树的距离。
\[\begin{aligned}
\operatorname{ans}_{u} & =(n-1)(n-1)!+\sum_{\left\{a_{i}\right\}} \sum_{i=1}^{n-1}(n-i) f\left(a_{i},\left\{a_{0}, \ldots, a_{i-1}\right\}\right) \\
& =(n-1)(n-1)!+\sum_{v \neq u} \sum_{i=1}^{n-1}(n-i) \sum_{|A|=i, u \in A} f(v, A)(i-1)!(n-1-i)! \\
& =(n-1)(n-1)!+\sum_{v \neq u} \sum_{i=1}^{n-1}(n-i)!(i-1)!\sum_{|A|=i, u \in A} \sum_{w \in \operatorname{anc}_{v}}[\operatorname{Subtree}(w) \cap A=\varnothing] \\
& =(n-1)(n-1)!+\sum_{v \neq u} \sum_{i=1}^{n-1}(n-i)!(i-1)!\sum_{w \in \operatorname{anc}_{v}}\binom{n-1-\operatorname{siz}_{w}}{i-1} \\
& =(n-1)(n-1)!+\sum_{w \neq u} \sum_{i=1}^{n-1}(n-i)!(i-1)!\binom{n-1-\operatorname{siz}_{w}}{i-1} \operatorname{siz}_{w}
\end{aligned}
\]
考虑预处理出 \(g_m=\sum_{i=1}^{n-1}(n-i)!(i-1)!{n-1-m\choose i-1}m\),于是可以换根求解。
\[\begin{aligned}
g_m&=\sum_{i=1}^{n-1}(n-i)!(i-1)!{n-1-m\choose i-1}m\\
&=m(n-m-1)!\sum_{i=1}^{n-1}\frac{(n-i)!}{(n-m-i)!}\\
&=m(n-m-1)!m!\sum_{i=1}^{n-1}{n-i\choose m}\\
&=m(n-m-1)!m!{n\choose m+1}\\
&=\frac{m}{m+1}n!
\end{aligned}
\]
时间复杂度线性对数。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e6+5,mod=998244353;
il int pls(int x,int y){
return x+y<mod?x+y:x+y-mod;
}
il void add(int &x,int y){
x=pls(x,y);
}
il int mns(int x,int y){
return x<y?x-y+mod:x-y;
}
il void sub(int &x,int y){
x=mns(x,y);
}
int n,fac[maxn],g[maxn],ans,sz[maxn],sum[maxn];
vector<int> e[maxn];
il int qpow(int x,int y=mod-2){
int res=1;
while(y){
if(y&1){
res=res*1ll*x%mod;
}
x=x*1ll*x%mod,y>>=1;
}
return res;
}
il void dfs1(int u,int fa){
sz[u]=1;
for(int v:e[u]){
if(v==fa){
continue;
}
dfs1(v,u);
add(ans,g[sz[v]]);
sz[u]+=sz[v];
}
}
il void dfs2(int u,int fa){
for(int v:e[u]){
if(v==fa){
continue;
}
add(ans,g[sz[1]-sz[v]]);
sub(ans,g[sz[v]]);
sum[v]=((n-1)*1ll*fac[n-1]+ans)%mod;
dfs2(v,u);
add(ans,g[sz[v]]);
sub(ans,g[sz[1]-sz[v]]);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
e[u].pb(v),e[v].pb(u);
}
fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=fac[i-1]*1ll*i%mod;
}
for(int i=1;i<n;i++){
g[i]=i*1ll*fac[n]%mod*qpow(i+1)%mod;
}
dfs1(1,0);
sum[1]=((n-1)*1ll*fac[n-1]+ans)%mod;
dfs2(1,0);
for(int i=1;i<=n;i++){
cout<<sum[i]<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}