【比赛记录】2025CSP-S模拟赛7

看似如此,但是考试的只有 \(5\) 个人(

A. k

考虑从第 \(l\) 层的点 \(x\) 走到第 \(r\) 层的点 \(y\) 的过程,一定是 \(x\to \text{第 l 层的某个门}\to\text{第 r-1 层的某个门}\to y\)。门与门之间的距离显然可以 dp,具体地,另 \(dp_{i,0/1}\) 表示走到第 \(i\) 层朝上 / 下的门的最短路。显然可以用线段树维护扩展矩阵乘来预处理。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5;
int n,m,px[maxn][2],py[maxn][2];
struct juz{
	int mat[2][2];
	juz(){
		memset(mat,0x3f,sizeof mat);
	}
	il int*operator[](int x){
		return mat[x];
	}
	il juz operator*(juz x)const{
		juz res;
		for(int i=0;i<=1;i++){
			for(int j=0;j<=1;j++){
				for(int k=0;k<=1;k++){
					res[i][j]=min(res[i][j],mat[i][k]+x[k][j]);
				}
			}
		}
		return res;
	}
}tr[maxn<<2];
il int dis(int x1,int y1,int x2,int y2){
	return abs(x1-x2)+abs(y1-y2);
}
il void pushup(int id){
	tr[id]=tr[lid]*tr[rid];
}
il void build(int id,int l,int r){
	if(l==r){
		tr[id][0][0]=dis(px[l-1][0]+1,py[l-1][0],px[l][0],py[l][0])+1;
		tr[id][0][1]=dis(px[l-1][0]+1,py[l-1][0],px[l][1],py[l][1])+1;
		tr[id][1][0]=dis(px[l-1][1],py[l-1][1]+1,px[l][0],py[l][0])+1;
		tr[id][1][1]=dis(px[l-1][1],py[l-1][1]+1,px[l][1],py[l][1])+1;
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
il juz query(int id,int L,int R,int l,int r){
	if(L>=l&&R<=r){
		return tr[id];
	}
	int mid=(L+R)>>1;
	if(r<=mid){
		return query(lid,L,mid,l,r);
	}
	if(l>mid){
		return query(rid,mid+1,R,l,r);
	}
	return query(lid,L,mid,l,r)*query(rid,mid+1,R,l,r);
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<n;i++){
		cin>>px[i][0]>>py[i][0]>>px[i][1]>>py[i][1];
	}
	if(n>2){
		build(1,2,n-1);
	}
	cin>>m;
	while(m--){
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		int t1=max(x1,y1),t2=max(x2,y2);
		if(t1>t2){
			swap(x1,x2),swap(y1,y2),swap(t1,t2);
		}
		if(t1==t2){
			int ans=dis(x1,y1,x2,y2);
			cout<<ans<<"\n";
		}
		else if(t1+1==t2){
			int ans=min(dis(x1,y1,px[t1][0],py[t1][0])+1+dis(px[t1][0]+1,py[t1][0],x2,y2),
						dis(x1,y1,px[t1][1],py[t1][1])+1+dis(px[t1][1],py[t1][1]+1,x2,y2));
			cout<<ans<<"\n";
		}
		else{
			juz res=query(1,2,n-1,t1+1,t2-1);
			int ans=min({dis(x1,y1,px[t1][0],py[t1][0])+res[0][0]+1+dis(px[t2-1][0]+1,py[t2-1][0],x2,y2),
						 dis(x1,y1,px[t1][0],py[t1][0])+res[0][1]+1+dis(px[t2-1][1],py[t2-1][1]+1,x2,y2),
						 dis(x1,y1,px[t1][1],py[t1][1])+res[1][0]+1+dis(px[t2-1][0]+1,py[t2-1][0],x2,y2),
						 dis(x1,y1,px[t1][1],py[t1][1])+res[1][1]+1+dis(px[t2-1][1],py[t2-1][1]+1,x2,y2)});
			cout<<ans<<"\n";
		}
	}
	return 0;
}
}
signed main(){return asbt::main();}

B. kk

设朝下照到第 \(i\) 块板的光为 \(f_i\),向上照到的为 \(g_i\)。于是我们有 \(2n\) 个方程:

\[\begin{cases} \begin{aligned} &f_1=1\\ &g_n=0\\ &\forall i\in[1,n-1],a_if_i+b_ig_i=f_{i+1}\\ &\forall i\in[2,n],b_if_i+a_ig_i=g_{i-1} \end{aligned} \end{cases} \]

于是我们可以高斯消元,答案为 \(a_nf_n\),能得 \(50pts\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int mod=1e9+7;
il int qpow(int x,int y){
	int res=1;
	while(y){
		if(y&1){
			res=res*1ll*x%mod;
		}
		x=x*1ll*x%mod,y>>=1;
	}
	return res;
}
const int inv100=qpow(100,mod-2);
int n,a[105],b[105],f[205][205];
il void gsjd(int x){
	for(int i=1;i<=x;i++){
		int tmp=qpow(f[i][i],mod-2);
		for(int j=1;j<=x;j++){
			if(j==i){
				continue;
			}
			for(int k=i+1;k<=x+1;k++){
				int t=f[i][k]*1ll*f[j][i]%mod*tmp%mod;
//				cout<<i<<" "<<j<<" "<<k<<" "<<t<<"\n";
//				cout<<f[i][k]<<" "<<f[j][i]<<" "<<tmp<<"\n";
				(f[j][k]+=mod-t)%=mod;
			}
		}
//		for(int i=1;i<=n*2;i++){
//			for(int j=1;j<=n*2+1;j++){
//				printf("%10d ",f[i][j]);
//			}
//			puts("");
//		}
//		puts("--------------------------------------------------------------");
	}
	for(int i=1;i<=x;i++){
		f[i][x+1]=f[i][x+1]*1ll*qpow(f[i][i],mod-2)%mod;
	}
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];
		a[i]=a[i]*1ll*inv100%mod;
		b[i]=b[i]*1ll*inv100%mod;
	}
	for(int i=1;i<=n;i++){
		if(i<n){
			f[i+1][i]=a[i],f[i+1][n+i]=b[i],f[i+1][i+1]=mod-1;
		}
		if(i>1){
			f[n+i-1][i]=b[i],f[n+i-1][n+i]=a[i],f[n+i-1][n+i-1]=mod-1;
		}
	}
	f[1][1]=1,f[1][2*n+1]=1;
	f[2*n][2*n]=1;
//	for(int i=1;i<=n*2;i++){
//		for(int j=1;j<=n*2+1;j++){
//			printf("%10d ",f[i][j]);
//		}
//		puts("");
//	}
//	puts("--------------------------------------------------------------");
	gsjd(n*2);
	cout<<f[n][n*2+1]*1ll*a[n]%mod;
	return 0;
}
}
int main(){return asbt::main();}

考虑如何递推。首先将第 \(4\) 个式子变一下形:

\[\forall i\in[1,n-1],g_i=b_{i+1}f_{i+1}+a_{i+1}g_{i+1} \]

那么由于 \(g_n=0\),所以 \(g_{n-1}=b_ng_n\)

\(g_i\) 再带入第三个式子,就可以求出 \(f_i\)\(f_n\) 的多少倍。于是倒着递推,根据 \(f_1=1\) 就可以得出 \(f_n\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=5e5+5,mod=1e9+7;
il int qpow(int x,int y){
	int res=1;
	while(y){
		if(y&1){
			res=res*1ll*x%mod;
		}
		x=x*1ll*x%mod,y>>=1;
	}
	return res;
}
const int inv100=qpow(100,mod-2);
int n,a[maxn],b[maxn],f[maxn],g[maxn];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];
		a[i]=a[i]*1ll*inv100%mod;
		b[i]=b[i]*1ll*inv100%mod;
	}
	f[n]=1,g[n]=0;
	for(int i=n-1;i;i--){
		g[i]=(b[i+1]*1ll*f[i+1]+a[i+1]*1ll*g[i+1])%mod;
		f[i]=(f[i+1]-b[i]*1ll*g[i]%mod+mod)*qpow(a[i],mod-2)%mod;
	}
	cout<<qpow(f[1],mod-2)*1ll*a[n]%mod;
	return 0;
}
}
int main(){return asbt::main();}

C. kkk

\(f_{i,j}\) 表示长为 \(i\),仅最后一位为 \(j\) 的序列的数量,\(g_i\) 表示长为 \(i\) 的序列的数量。考虑到一个数字 \(j\) 时,设小于等于 \(j\) 的数有 \(sum_j\) 个,那么有 \(i\le sum_j\)。那么我们先算出 \(f_{i,j}\) 的值,再累加贡献给 \(g_i\) 即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=3e3+5,mod=1e9+7;
int n,a[maxn],cnt[maxn],sum[maxn],f[maxn][maxn],g[maxn];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		cnt[a[i]]++;
	}
	for(int i=1;i<=n;i++){
		sum[i]=sum[i-1]+cnt[i];
	}
	g[0]=1;
	for(int j=1;j<=n;j++){
		if(!cnt[j]){
			continue;
		}
		for(int i=1;i<=sum[j];i++){
			f[i][j]=g[i-1];
		}
		int tmp=0;
		for(int i=1;i<=sum[j];i++){
			(tmp+=f[i][j])%=mod;
			(g[i]+=tmp)%=mod;
		}
	}
	for(int i=1;i<=n;i++){
		cout<<g[i]<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}

D. kkkk

\(f_i\) 表示钦定有 \(i\)\(|a_i-i|=K\) 的方案数。那么答案即为 \(\sum_{i=0}^{n}(-1)^{i}f_i\)

考虑如何求 \(f_i\),首先设出 \(2n\) 个点,分别为 \(1,2,\dots,n,a_1,a_2,\dots,a_n\)\(i\)\(a_j\) 连边表示 \(a_j=i\)。那么我们要做的其实就是连出 \(n\) 条形如 \(i\leftrightarrow a_j\) 的边,使每个点的度都为 \(1\)。考虑 \(K\) 的限制,即形如 \(i\leftrightarrow a_{i+K}\)\(i\leftrightarrow a_{i-K}\) 的边不能存在,意思就是我们钦定的就是这样的边。这些边显然会将这 \(2n\) 个点连成若干条链。那么我们将这些链放在一起 dp。具体地,设 \(dp_{i,j,0/1}\) 表示前 \(i\) 个点中连了 \(j\) 条边,其中 \(i-1\)\(i\) 有没有连边的方案数。那么显然有:

\[\begin{align*} &dp_{i,j,0}=dp_{i-1,j,0}+dp_{i-1,j,1}\\ &dp_{i,j,1}=dp_{i-1,j-1,0}\\ &f_i=(dp_{2n,i,0}+dp_{2n,i,1})\times(n-i)! \end{align*} \]

于是就结束了。注意上一条链的链尾和这一条链的链头间是不能连边的。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=2e3+5,mod=924844033;
int n,m,f[maxn<<1][maxn][2];
bool vis[maxn][2],ban[maxn<<1];
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	int cnt=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=1;j++){
			if(vis[i][j]){
				continue;
			}
			int x=i,d=j;
			ban[cnt+1]=1;
			do{
				cnt++;
				vis[x][d]=1;
				x+=m,d^=1;
			}while(x<=n);
		}
	}
	for(int i=1;i<=n<<1;i++){
		f[i][0][0]=1;
		for(int j=1;j<=n;j++){
			f[i][j][0]=(f[i-1][j][0]+f[i-1][j][1])%mod;
			if(!ban[i]){
				f[i][j][1]=f[i-1][j-1][0];
			}
		}
	}
	int ans=0;
	for(int i=n,fac=1;~i;i--){
		if(i&1){
			(ans+=mod-(f[n<<1][i][0]+f[n<<1][i][1])*1ll*fac%mod)%=mod;
		}
		else{
			(ans+=(f[n<<1][i][0]+f[n<<1][i][1])*1ll*fac%mod)%=mod;
		}
		fac=fac*1ll*(n-i+1)%mod;
	}
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2025-05-18 14:33  zhangxy__hp  阅读(36)  评论(0)    收藏  举报