【比赛记录】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\) 个方程:
于是我们可以高斯消元,答案为 \(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\) 个式子变一下形:
那么由于 \(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\) 有没有连边的方案数。那么显然有:
于是就结束了。注意上一条链的链尾和这一条链的链头间是不能连边的。
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();}

浙公网安备 33010602011771号