【比赛记录】2025 暑假集训模拟赛合集Ⅱ

2025CSP-S模拟赛35

A B C D Sum Rank
100 40 40 - 180 3/19

A. 114514

分别考虑 \(b\) 的每一位可以填什么。因为 \(a\) 是字典序最小的,所以对于每一位 \(a\) 不能有更小的选择,于是 \(b_i\le a_i\) 且要求 \([b_i,a_i]\)\(a_i\) 及之前都出现过。用链表可以做到线性复杂度。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=4e6+5,mod=1e9+7;
int n,a[maxn],pre[maxn],nxt[maxn];
int main(){
	freopen("trans.in","r",stdin);
	freopen("trans.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=0;i<=4e6;i++){
		nxt[i]=i+1,pre[i+1]=i;
	}
	int ans=1;
	for(int i=1;i<=n;i++){
		ans=ans*1ll*(a[i]-pre[a[i]])%mod;
		nxt[pre[a[i]]]=nxt[a[i]];
		pre[nxt[a[i]]]=pre[a[i]];
	}
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}

B. 沉默乐团

容易发现,我们只需判断不交的前后缀即可。考虑前缀和后缀都是严格单增的,于是考虑将它们归并起来,考察临项是否相等。考虑 DP,设 \(f_{i,j,k}\) 表示考虑到了 \(i\) 的前缀,\(j\) 的后缀,后缀 \(-\) 前缀 \(=k\) 的方案数,于是有转移:

\[f_{i,j,k}\to f_{i+1,j,k-x}\quad x\in[l_{i+1},r_{i+1}]\land k>0\\ f_{i,j,k}\to f_{i,j-1,k+x}\quad x\in[l_{j-1},r_{j-1}]\land k\le0 \]

注意 \(k\ne0\),初始值 \(f_{0,n+1,0}=1\)。答案即为 \(\sum_{i=0}^{n}f_{i,i+1,k}\)。差分优化,时间复杂度 \(O(n^2m)\)

Code
#include<bits/stdc++.h>
#define il inline
using namespace std;
namespace asbt{
const int V=2e3+5,mod=1e9+7;
il int pls(int x,int y){
	return x+y<mod?x+y:x+y-mod;
}
il int sub(int x,int y){
	return x<y?x-y+mod:x-y;
}
il void add(int &x,int y){
	x=pls(x,y);
}
il void mns(int &x,int y){
	x=sub(x,y);
}
int n,ll[55],rr[55],f[55][55][V<<1];
il void upd(int *f,int l,int r,int v){
	add(f[l+V],v),mns(f[r+V+1],v);
}
int main(){
	freopen("orchestra.in","r",stdin);
	freopen("orchestra.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>ll[i]>>rr[i];
	}
	upd(f[0][n+1],0,0,1);
	for(int len=n+2;len>=2;len--){
		for(int i=0,j=len-1;j<=n+1;i++,j++){
			for(int k=-2000;k<=2000;k++){
				add(f[i][j][k+V],f[i][j][k+V-1]);
				if(k>0){
					int l=k-rr[i+1],r=k-ll[i+1];
					if(l>0||r<0){
						upd(f[i+1][j],l,r,f[i][j][k+V]);
					}
					else{
						upd(f[i+1][j],l,-1,f[i][j][k+V]);
						upd(f[i+1][j],1,r,f[i][j][k+V]);
					}
				}
				else{
					int l=k+ll[j-1],r=k+rr[j-1];
					if(l>0||r<0){
						upd(f[i][j-1],l,r,f[i][j][k+V]);
					}
					else{
						upd(f[i][j-1],l,-1,f[i][j][k+V]);
						upd(f[i][j-1],1,r,f[i][j][k+V]);
					}
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<=n;i++){
		for(int j=-2000;j<=2000;j++){
			add(ans,f[i][i+1][j+V]);
		}
	}
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}

C. 深黯「军团」

考虑将逆序对拆成每一位的贡献,即每一位计算它后面有多少 \(<a_i\)。打个表出来长这样:

打表
1 2 3 4 |0 0 0 0
1 2 4 3 |0 0 1 0
1 3 2 4 |0 1 0 0
1 3 4 2 |0 1 1 0
1 4 2 3 |0 2 0 0
1 4 3 2 |0 2 1 0
2 1 3 4 |1 0 0 0
2 1 4 3 |1 0 1 0
2 3 1 4 |1 1 0 0
2 3 4 1 |1 1 1 0
2 4 1 3 |1 2 0 0
2 4 3 1 |1 2 1 0
3 1 2 4 |2 0 0 0
3 1 4 2 |2 0 1 0
3 2 1 4 |2 1 0 0
3 2 4 1 |2 1 1 0
3 4 1 2 |2 2 0 0
3 4 2 1 |2 2 1 0
4 1 2 3 |3 0 0 0
4 1 3 2 |3 0 1 0
4 2 1 3 |3 1 0 0
4 2 3 1 |3 1 1 0
4 3 1 2 |3 2 0 0
4 3 2 1 |3 2 1 0

发现每一列都在以 \((n-i+1)!\) 为循环周期循环,且第 \(i+1\) 列经过一个循环周期后第 \(i\) 列答案加一。具体的证明:后 \(n-i\) 位全排列一次过程中 \(i\) 的答案不会变,而将 \(i\) 和后面某个位置交换一定会使逆序对加一。

于是我们考虑对于每一列单独计算。以下将相同的数组成的段叫一块。首先是一个散块,我们可以通过第 \(i+1\) 列第一个循环节的开头位置算出它的长度。然后是几个整块,它们和前面那个散块组成一个散循环节,这些整块直接暴力枚举算出。然后是几个整循环节,直接等差数列求和即可 \(O(1)\) 算出。后面又是一个散循环节,暴力算即可。

考虑时间复杂度,\(20!>10^{18}\),于是 \(n-i\ge19\) 后没有整循环节,\(n-i\ge20\) 后没有整块。所以时间复杂度就是 \(O(n\log n)\)

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=5e5+5,inf=2e18;
int n,m,mod,a[maxn],fac[maxn];
struct{
	#define lowbit(x) (x&-x)
	int tr[maxn];
	il void add(int p,int v){
		for(;p<=n;p+=lowbit(p)){
			tr[p]+=v;
		}
	}
	il int query(int p){
		int res=0;
		for(;p;p-=lowbit(p)){
			res+=tr[p];
		}
		return res;
	}
	#undef lowbit
}F;
int main(){
	freopen("army.in","r",stdin);
	freopen("army.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m>>mod;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	fac[0]=1;
	for(int i=1;i<=19;i++){
		fac[i]=fac[i-1]*i;
//		cout<<fac[i]<<" ";
	}
//	cout<<"\n";
	for(int i=20;i<=n;i++){
		fac[i]=inf;
	}
	int ans=0;
	for(int i=n,lst0=1,chu,len;i;i--){
//		cout<<lst0<<"\n";
		chu=F.query(a[i]);
		if(lst0>m){
			(ans+=m%mod*chu)%=mod;
			goto togo;
		}
		(ans+=(lst0-1)%mod*chu)%=mod;
		if(lst0>1){
			chu++;
		}
		for(;chu<=n-i;chu++){
			if(lst0+fac[n-i]-1>m){
				(ans+=(m-lst0+1)%mod*chu)%=mod;
				lst0=inf;
				goto togo;
			}
			(ans+=fac[n-i]%mod*chu)%=mod;
			lst0+=fac[n-i];
		}
		(ans+=(m-lst0+1)/fac[n-i+1]%mod*(fac[n-i]%mod)%mod*((n-i+1)*(n-i)/2%mod))%=mod;
		len=(m-lst0+1)%fac[n-i+1];
		for(int j=0;;j++){
			if(len<fac[n-i]){
				(ans+=len%mod*j)%=mod;
				break;
			}
			(ans+=fac[n-i]%mod*j)%=mod;
			len-=fac[n-i];
		}
		togo:;
		F.add(a[i],1);
//		cout<<ans<<"\n";
	}
	cout<<ans;
	return 0;
}
}
signed main(){return asbt::main();}
/*
10 6 998244353
10 6 2 9 8 3 7 1 5 4 
*/

D. 终末螺旋

2025CSP-S模拟赛36

A B C D Sum Rank
40 10 20 12 82 21/23

A. 购买饮料

首先判断 \(-1\) 情况:如果能买得起 \(a\) 瓶,并且 \(b\) 块钱也能买得起 \(a\) 瓶,那么输出 \(-1\)。然后我们就不断地买 \(a\) 瓶并换钱直到买不起 \(a\) 瓶即可。时间复杂度 \(O(T)\)

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
int T,n,m,a,b;
int main(){
	freopen("buy.in","r",stdin);
	freopen("buy.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m>>a>>b;
		if(n<a*m){
			cout<<n/m<<'\n';
		}
		else if(b>=a*m){
			cout<<-1<<'\n';
		}
		else{
			int t=(n-a*m)/(a*m-b)+1,ans=t*a;
			n-=t*(a*m-b),ans+=n/m;
			cout<<ans<<'\n';
		}
	}
	return 0;
}
}
signed main(){return asbt::main();}

B. 多边形

首先判断是否有一种颜色只有一个点,如果有,那么将这个点和其他所有点相连即可。否则,一定有相邻的三个颜色不同的点,将它们分成一个小三角形,递归下去即可。可以发现按照这样的构造一定有解。需要链表和栈。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e6+5;
int n,pre[maxn],nxt[maxn],_1=1,c1,c2,c3;
bool ban[maxn];
string s;
stack<int> stk,R,G,B;
il bool chk(int x){
	char hp[3]={s[pre[x]],s[x],s[nxt[x]]};
	sort(hp,hp+3);
	return hp[0]=='B'&&hp[1]=='G'&&hp[2]=='R';
}
int main(){
//	freopen("B4.in","r",stdin);
	freopen("polygon.in","r",stdin);
	freopen("polygon.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>s;
	s=" "+s;
	for(int i=1;i<=n;i++){
		pre[i]=i==1?n:i-1;
		nxt[i]=i==n?1:i+1;
		if(chk(i)){
			stk.push(i);
//			cout<<i<<'\n';
		}
		switch(s[i]){
		case 'R':{
			R.push(i),c1++;
			break;
		}
		case 'G':{
			G.push(i),c2++;
			break;
		}
		default:{
			B.push(i),c3++;
			break;
		}
	}
	}
	while(n>3){
		if(c1==1){
			while(ban[R.top()]){
				R.pop();
			}
			_1=R.top();
		}
		else if(c2==1){
			while(ban[G.top()]){
				G.pop();
			}
			_1=G.top();
		}
		else if(c3==1){
			while(ban[B.top()]){
				B.pop();
			}
			_1=B.top();
		}
		else{
			goto togo;
		}
		for(int i=nxt[nxt[_1]];;i=nxt[i]){
			cout<<i<<' '<<_1<<'\n';
			if(i==pre[pre[_1]]){
				break;
			}
		}
		break;
		togo:;
		while(stk.size()){
			int x=stk.top();
			stk.pop();
			if(ban[x]||!chk(x)){
				continue;
			}
			cout<<pre[x]<<' '<<nxt[x]<<'\n';
			n--,ban[x]=1;
			switch(s[x]){
				case 'R':{
					c1--;
					break;
				}
				case 'G':{
					c2--;
					break;
				}
				default:{
					c3--;
					break;
				}
			}
			if(x==_1){
				_1=nxt[x];
			}
//			cout<<"a "<<s[x]<<' '<<s[pre[x]]<<' '<<s[nxt[x]]<<'\n';
			nxt[pre[x]]=nxt[x];
			pre[nxt[x]]=pre[x];
			if(chk(pre[x])){
				stk.push(pre[x]);
			}
			if(chk(nxt[x])){
				stk.push(nxt[x]);
			}
			break;
		}
//		cout<<'\n';
//		cout<<n<<' '<<c1<<' '<<c2<<' '<<c3<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}

C. 二分图最大权匹配

,神秘网络流。

D. 飞毯

,神秘构造。

2025CSP-S模拟赛37

A B C D Sum Rank
100 - 25 - 125 19/25

A. 新的阶乘

我们需要一个线性做法,因此暴力枚举每个数的每一个质因子是无法接受的,考虑用欧拉筛找出每个数的最小质因子 \(\operatorname{mpf}(x)\),将 \(x\) 的幂次下放给 \(\operatorname{mpf}(x)\)\(\frac{x}{\operatorname{mpf}(x)}\) 即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e7+5;
int n,prn,prm[maxn],mpf[maxn];
ll fac[maxn];
bool npr[maxn];
il void euler(int n=1e7){
	for(int i=2;i<=n;i++){
		if(!npr[i]){
			prm[++prn]=i;
			mpf[i]=i;
		}
		for(int j=1;j<=prn&&i*1ll*prm[j]<=n;j++){
			npr[i*prm[j]]=1;
			mpf[i*prm[j]]=prm[j];
			if(i%prm[j]==0){
				break;
			}
		}
	}
}
int main(){
//	freopen("my.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	euler();
	cin>>n;
	for(int i=2;i<=n;i++){
		fac[i]=n-i+1;
	}
	for(int i=n;i>1;i--){
//		cout<<mpf[i]<<" ";
		if(npr[i]){
			fac[mpf[i]]+=fac[i];
			fac[i/mpf[i]]+=fac[i];
		}
	}
	bool flag=0;
	for(int i=1;i<=prn;i++){
		if(fac[prm[i]]){
			if(!flag){
				cout<<"f("<<n<<")=";
				flag=1;
			}
			else{
				cout<<'*';
			}
			cout<<prm[i];
			if(fac[prm[i]]>1){
				cout<<'^'<<fac[prm[i]];
			}
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

B. 博弈树

首先可以发现如果先手在直径端点上那么必胜。接下来双方一定不能走到直径端点上,否则对方再走到另一个端点就输了。因此我们可以直接将所有直径端点删掉。那么在删后的这棵树上,双方一定还是不能走到直径端点上,否则另一方走到另一个端点上后自己将不得不走到原树直径端点上。那么一层层删点,如果最后剩下一个点了那么先手必败,否则必胜。考虑这个条件等价于什么,发现就是如果起始点在直径中点那么先手必败,否则先手必胜,dfs 预处理即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,m,d1,d2,ans,dep[maxn],mxd[maxn],des[maxn];
vector<int> e[maxn];
il void dfs1(int u,int fa){
	mxd[u]=dep[u]=dep[fa]+1,des[u]=u;
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs1(v,u);
		if(mxd[v]>mxd[u]){
			mxd[u]=mxd[v],des[u]=des[v];
		}
	}
}
il void dfs2(int u,int fa){
	des[dep[u]]=u;
	if(u==d2){
		ans=des[(dep[u]+1)>>1];
	}
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs2(v,u);
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	dfs1(1,0),d1=des[1];
	dfs1(d1,0),d2=des[d1];
	if(dep[d2]&1){
		dfs2(d1,0);
	}
	while(m--){
		int u;
		cin>>u;
		cout<<(u==ans?"Bob":"Alice")<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}

C. 划分

发现我们的划分一定是一段长为 \(n-k+1\) 的和 \(k-1\) 段长为 \(1\) 的。考虑每个数对应的位权,那么我们一定是要找到最大的一段 \(n-k+1\),哈希加二分求出 LCP 即可比较大小。考虑方案数,发现因为 \(1\) 的数量是固定的,所以最后一位是 \(0\) 还是 \(1\) 都是无所谓的,所以找到有多少个长为 \(n-k+1\) 的段与最优段的前 \(n-k\) 位相同即可。注意 \(n=k\) 时方案数为 \(1\)。但是还有一个问题,如果这个最优段有前导零,那么方案数是会变多的,换句话说如果前 \(k-1\) 位都是 \(0\) 那么一定是在前导零中随便分 \(\ge k-1\) 次,组合数计算即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ull unsigned ll
using namespace std;
namespace asbt{
const int maxn=2e6+5,mod=998244353;
const ull bas1=131;
const ll bas2=233,mod2=1e9+7;
int n,m,fac[maxn],inv[maxn];
ull ha1[maxn],pw1[maxn];
ll ha2[maxn],pw2[maxn];
string s;
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 int C(int x,int y){
	if(x<y||y<0){
		return 0;
	}
	return fac[x]*1ll*inv[y]%mod*inv[x-y]%mod;
}
il void chk(){
	if(n==m){
		int ans=0;
		for(int i=1;i<=n;i++){
			ans+=s[i]^48;
		}
		cout<<ans<<' '<<1;
		exit(0);
	}
	for(int i=1;i<m;i++){
		if(s[i]=='1'){
			return ;
		}
	}
	int ans=0,pos=n;
	for(int i=1;i<=n;i++){
		ans=(ans*2+(s[i]^48))%mod;
		if(pos==n&&s[i]=='1'){
			pos=i;
		}
	}
	fac[0]=1;
	for(int i=1;i<=n;i++){
		fac[i]=fac[i-1]*1ll*i%mod;
	}
	inv[n]=qpow(fac[n]);
	for(int i=n;i;i--){
		inv[i-1]=inv[i]*1ll*i%mod;
	}
	int res=0;
	for(int i=m-1;i<pos;i++){
		(res+=C(pos-1,i))%=mod;
	}
	cout<<ans<<' '<<res;
	exit(0);
}
il ull Ha1(int l,int r){
	return ha1[r]-ha1[l-1]*pw1[r-l+1];
}
il ll Ha2(int l,int r){
	return (ha2[r]-ha2[l-1]*pw2[r-l+1]%mod2+mod2)%mod2;
}
il bool Eq(int l1,int r1,int l2,int r2){
	return Ha1(l1,r1)==Ha1(l2,r2)&&Ha2(l1,r1)==Ha2(l2,r2);
}
il int lcp(int p,int q){
	int l=0,r=n-m+1;
	while(l<r){
		int mid=(l+r+1)>>1;
		if(Eq(p,p+mid-1,q,q+mid-1)){
			l=mid;
		}
		else{
			r=mid-1;
		}
	}
	return l;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m>>s;
	s=" "+s;
	chk();
	pw1[0]=pw2[0]=1;
	for(int i=1;i<=n;i++){
		pw1[i]=pw1[i-1]*bas1;
		pw2[i]=pw2[i-1]*bas2%mod2;
		ha1[i]=ha1[i-1]*bas1+s[i];
		ha2[i]=(ha2[i-1]*bas2+s[i])%mod2;
	}
	int p=1;
	for(int i=2;i<=m;i++){
		int t=lcp(p,i);
		if(t<n-m+1&&s[p+t]<s[i+t]){
			p=i;
		}
	}
	int ans=0,cnt=0;
	for(int i=p,j=1;j<=n-m+1;i++,j++){
		ans=(ans*2+(s[i]^48))%mod;
	}
	for(int i=1;i<p;i++){
		ans+=s[i]^48;
	}
	for(int i=p+n-m+1;i<=n;i++){
		ans+=s[i]^48;
	}
	ans%=mod;
	for(int i=1;i<=m;i++){
		if(Eq(i,i+n-m-1,p,p+n-m-1)){
			cnt++;
		}
	}
	cout<<ans<<' '<<cnt;
	return 0;
}
}
int main(){return asbt::main();}

D. 灯笼

考虑 DP。设 \(f_{l,r}\) 表示可以到达的高度区间为 \([l,r]\) 的最小花费,每次枚举出发点 \(i\) 进行一次 DP,时间复杂度 \(O(nk^3)\)

考虑去掉枚举 \(i\) 的操作,考虑转换定义与转移顺序(转置)。设 \(f_{i,l,r}\) 表示当前在 \(i\),高度区间为 \([l,r]\),将 \([1,n]\) 走遍的最小花费,考虑优化状态,容易发现 \([l,r]\) 其实是 \([a_u,b_v]\),这样就可以不用记录 \(i\),于是设 \(f_{u,v}\) 表示合法海拔区间为 \([a_u,b_v]\),且区间经过了 \(u\)\(v\),将 \([1,n]\) 走遍的最小花费,于是有转移:

  • \(f_{u,v}\leftarrow f_{u,p}+c_p\)
  • \(f_{u,v}\leftarrow f_{p,v}+c_p\)
  • \(f_{u,v}\leftarrow f_{p,p}+c_p\)

此时的时间复杂度为 \(O(nk^2)\),考虑优化转移。我们显然要按照 \(a\) 升序的顺序枚举 \(u\)\(b\) 降序的顺序枚举 \(v\),那么对于 \(v'<v\),如果 \(f_{u,p}\) 不能向 \(f_{u,v}\) 转移,那么一定不能向 \(f_{u,v'}\) 转移。于是用小根堆对每个 \(u\) 维护合法的 \(f_{u,p}+c_p\) 即可。对于第三个转移,将其分步成 \(f_{p,p}\to f_{u,p}/f_{p,v}\to f_{u,v}\),特判即可。时间复杂度 \(O(k^2\log k)\)

Code
#include<bits/stdc++.h>
#define il inline
#define pii pair<int,int>
#define mp make_pair
#define fir first
#define sec second
using namespace std;
namespace asbt{
const int maxn=2e3+5,inf=0x7f7f7f7f;
int n,m,h[maxn],p[maxn],c[maxn],a[maxn],b[maxn];
int I[maxn],J[maxn],ll[maxn][2],rr[maxn][2],f[maxn][maxn];
priority_queue<pii> q1[maxn],q2[maxn];
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>h[i];
	}
	for(int i=1;i<=m;i++){
		cin>>p[i]>>c[i]>>a[i]>>b[i];
		I[i]=J[i]=i;
	}
	sort(I+1,I+m+1,[](int x,int y){return a[x]<a[y];});
	sort(J+1,J+m+1,[](int x,int y){return b[x]>b[y];});
	for(int i=1;i<=m;i++){
		ll[i][0]=ll[i][1]=rr[i][0]=rr[i][1]=p[i];
		while(ll[i][0]>1&&h[ll[i][0]-1]>=a[i]){
			ll[i][0]--;
		}
		while(rr[i][0]<n&&h[rr[i][0]+1]>=a[i]){
			rr[i][0]++;
		}
		while(ll[i][1]>1&&h[ll[i][1]-1]<=b[i]){
			ll[i][1]--;
		}
		while(rr[i][1]<n&&h[rr[i][1]+1]<=b[i]){
			rr[i][1]++;
		}
	}
	memset(f,0x7f,sizeof(f));
	for(int i=1;i<=m;i++){
		for(int j=1;j<=m;j++){
			int x=I[i],y=J[j];
			if(p[x]<ll[y][1]||p[x]>rr[y][1]||p[y]<ll[x][0]||p[y]>rr[x][0]){
				continue;
			}
			if(a[y]<a[x]&&b[x]>b[y]){
				continue;
			}
			int l=max(ll[x][0],ll[y][1]),r=min(rr[x][0],rr[y][1]);
			if(l==1&&r==n){
				f[x][y]=0;
			}
			else if(a[y]<a[x]){
				f[x][y]=f[y][y];
			}
			else if(b[x]>b[y]){
				f[x][y]=f[x][x];
			}
			else{
				while(q1[x].size()){
					int t=q1[x].top().sec;
					if(p[t]<l||p[t]>r||b[t]<a[x]||a[t]>b[y]){
						q1[x].pop();
					}
					else{
						break;
					}
				}
				if(q1[x].size()){
					f[x][y]=min(f[x][y],-q1[x].top().fir);
				}
				while(q2[y].size()){
					int t=q2[y].top().sec;
					if(p[t]<l||p[t]>r||b[t]<a[x]||a[t]>b[y]){
						q2[y].pop();
					}
					else{
						break;
					}
				}
				if(q2[y].size()){
					f[x][y]=min(f[x][y],-q2[y].top().fir);
				}
			}
			if(f[x][y]<inf){
				q1[x].push(mp(-f[x][y]-c[y],y));
				q2[y].push(mp(-f[x][y]-c[x],x));
			}
		}
	}
	for(int i=1;i<=m;i++){
		if(h[p[i]]<a[i]||h[p[i]]>b[i]||f[i][i]>=inf){
			cout<<-1<<'\n';
		}
		else{
			cout<<f[i][i]+c[i]<<'\n';
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

2025CSP-S模拟赛38

A B C D Sum Rank
80 20 10 - 110 9/21

A. 黑暗料理

原(bushi

首先可以发现所有的 \(1\) 只能留一个。然后我们考虑将和为质数的 \(x\)\(y\) 连边,此时 \(x+y\) 一定为奇数,也就是 \(x\)\(y\) 奇偶性不同,于是没有奇环。此时我们要求最大独立集,跑匈牙利即可。需要 Miller-Rabin。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int prm[]={2,3,5,7,11};
int T,n,m,a[755],mch[755];
bool vis[755];
vector<int> e[755];
il int qpow(int x,int y,int p){
	int res=1;
	while(y){
		if(y&1){
			res=res*1ll*x%p;
		}
		x=x*1ll*x%p,y>>=1;
	}
	return res;
}
il bool mlrb(int x){
	if(x<=11){
		for(int i:prm){
			if(x==i){
				return 1;
			}
		}
		return 0;
	}
	int t=x-1,k=0;
	while(t%2==0){
		t>>=1,k++;
	}
	for(int i:prm){
		int a=qpow(i,t,x);
		for(int j=1;j<=k;j++){
			int b=a*1ll*a%x;
			if(b==1&&a!=1&&a!=x-1){
				return 0;
			}
			a=b;
		}
		if(a!=1){
			return 0;
		}
	}
	return 1;
}
il bool dfs(int u){
	for(int v:e[u]){
		if(vis[v]){
			continue;
		}
		vis[v]=1;
		if(mch[v]==-1||dfs(mch[v])){
			mch[v]=u;
			return 1;
		}
	}
	return 0;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n,m=0;
		bool flag=0;
		for(int i=1,x;i<=n;i++){
			cin>>x;
			if(x>1){
				a[++m]=x;
			}
			else if(!flag){
				flag=1,a[++m]=x;
			}
		}
		for(int i=1;i<=m;i++){
			for(int j=i+1;j<=m;j++){
				if(mlrb(a[i]+a[j])){
					e[i].pb(j),e[j].pb(i);
//					cout<<i<<' '<<j<<'\n';
				}
			}
		}
		int ans=0;
		for(int i=1;i<=m;i++){
			mch[i]=-1;
		}
		for(int i=1;i<=m;i++){
			for(int j=1;j<=m;j++){
				vis[j]=0;
			}
			ans+=dfs(i);
		}
		cout<<m-ans/2<<'\n';
		for(int i=1;i<=m;i++){
			e[i].clear();
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

B. 爆炸

如果一个炸弹是被横着引爆的,那么贪心地想,它一定要竖着炸。于是对每一行和每一列设点,对于一个炸弹 \((x,y)\),将第 \(x\) 行和第 \(y\) 列连边,于是获得了若干连通块。考虑如果连通块内有环,那么随便引爆环上一个点,一定是能把这个连通块炸完的,直接计算答案即可。如果没有环,那么这一定是一棵树,我们要选择一条边,只保留它的一边,另一边舍去。显然舍去一个叶子节点是最优的,暴力枚举叶子即可。时间复杂度 \(O(nm)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=6e3+5;
int n,m,a,b,cnt,lfn,ans,fa[maxn],f[maxn][maxn],hp[maxn],lf[maxn];
bool tag[maxn],vis[maxn];
string s[maxn];
vector<int> e[maxn];
il int find(int x){
	return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void dfs1(int u){
	if(vis[u]){
		return;
	}
	hp[++cnt]=u;
	vis[u]=1;
	if(u<=n){
		for(int i=1;i<=m;i++){
			if(s[u][i]=='k'&&++f[u][i]==1){
				ans++;
			}
		}
	}
	else{
		for(int i=1;i<=n;i++){
			if(s[i][u-n]=='k'&&++f[i][u-n]==1){
				ans++;
			}
		}
	}
	for(int v:e[u]){
		dfs1(v);
	}
}
il void dfs2(int u,int fa){
	hp[++cnt]=u;
	if(e[u].size()==1){
		lf[++lfn]=u;
	}
	if(u<=n){
		for(int i=1;i<=m;i++){
			if(s[u][i]=='k'&&++f[u][i]==1){
				ans++;
			}
		}
	}
	else{
		for(int i=1;i<=n;i++){
			if(s[i][u-n]=='k'&&++f[i][u-n]==1){
				ans++;
			}
		}
	}
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs2(v,u);
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m>>a>>b;
	for(int i=1;i<=n+m;i++){
		fa[i]=i;
	}
	for(int i=1;i<=n;i++){
		cin>>s[i];
		s[i]=" "+s[i];
		for(int j=1;j<=m;j++){
			if(s[i][j]=='b'){
//				cout<<i<<' '<<n+j<<'\n';
				e[i].pb(n+j),e[n+j].pb(i);
				int u=find(i),v=find(n+j);
				if(u==v){
					tag[u]=1;
				}
				else{
					fa[u]=v,tag[v]|=tag[u];
				}
			}
		}
	}
//	for(int i=1;i<=n;i++){
//		cout<<s[i]<<'\n';
//	}
	int Ans=0;
	for(int i=1;i<=n+m;i++){
		if(find(i)==i&&e[i].size()){
//			cout<<i<<'\n';
			if(tag[i]){
				cnt=ans=0;
				dfs1(i);
				Ans=max(Ans,ans);
			}
			else{
				cnt=lfn=ans=0;
				dfs2(i,0);
				for(int j=1;j<=lfn;j++){
					int u=lf[j];
					if(u<=n){
						for(int i=1;i<=m;i++){
							if(s[u][i]=='k'&&f[u][i]--==1){
								ans--;
							}
						}
					}
					else{
						for(int i=1;i<=n;i++){
							if(s[i][u-n]=='k'&&f[i][u-n]--==1){
								ans--;
							}
						}
					}
					Ans=max(Ans,ans);
					if(u<=n){
						for(int i=1;i<=m;i++){
							if(s[u][i]=='k'&&++f[u][i]==1){
								ans++;
							}
						}
					}
					else{
						for(int i=1;i<=n;i++){
							if(s[i][u-n]=='k'&&++f[i][u-n]==1){
								ans++;
							}
						}
					}
				}
			}
			for(int j=1;j<=cnt;j++){
				int u=hp[j];
				if(u<=n){
					for(int i=1;i<=m;i++){
						f[u][i]=0;
					}
				}
				else{
					for(int i=1;i<=n;i++){
						f[i][u-n]=0;
					}
				}
			}
		}
	}
	cout<<Ans;
	return 0;
}
}
int main(){return asbt::main();}

C. 游戏

发现每个棋子是独立的,考虑算出 \(sg_{i,j}\) 表示棋子初始在 \((i,j)\) 时的 \(SG\) 值,将所有棋子的 \(SG\) 值异或起来就是总的 \(SG\) 值。

发现 \(sg_{i+1,j}\) 不受 \(sg_{i,j}\) 影响,于是倒序枚举每一行。然后对每一行进行分类讨论。记行号为 \(t\)

本行中有障碍

这一行被这些障碍分成了若干段。于是对于一个位置 \((t,i)\),它只有三种状态:

  1. \((t-1,i)\) 下来,可以往左、往右或往下走。

  2. \((t,i-1)\) 过来,可以往右或往下走。

  3. \((t,i+1)\) 过来,可以往左或往下走。

于是我们考虑预处理出 \(f_{0,i}\) 表示当前只可以往左或往下走时 \((t,i)\)\(SG\) 值,\(f_{1,i}\) 表示当前可以往右或往下走时 \((t,i)\)\(SG\) 值,于是 \(sg_{t,i}=\operatorname{mex}(f_{0,i-1},f_{1,i+1},sg_{t+1,i})\)

本行中没有障碍

考虑棋子初始在 \((t,i)\),如果第一步先往左走到了 \((t,i-1)\),那么就不能再往右走了,于是 \((t,i-1)\)\(SG\) 值需要 \((t,i-2)\)\(SG\) 值,进一步需要 \((t,i-3)\)\(SG\) 值……以此类推,我们最终需要的是 \((t,i+1)\)\(SG\) 值,此时只能往下走了,是显然的。但是这样我们单次求的时间复杂度是 \(O(m)\) 的,考虑优化。考虑从 \(i-1\) 一路走到 \(i+1\) 的过程,大概是这个样子:

C1

\(sg_{i,j}\) 的取值范围是 \([0,3]\),于是我们可以预处理出图中打勾的两个部分。

具体地,定义数组 \(g_{0/1/2/3,j,i}\)

  • \(g_{0,j,i}\) 表示 \((t,1)\)\(SG\) 值为 \(j\),棋子从 \(i\) 移动到 \(1\)\((t,i)\)\(SG\) 值。

  • \(g_{1,j,i}\) 表示 \((t,m)\)\(SG\) 值为 \(j\),棋子从 \(i\) 移动到 \(m\)\((t,i)\)\(SG\) 值。

  • \(g_{2,j,i}\) 表示 \((t,i)\)\(SG\) 值为 \(j\),棋子从 \(1\) 移动到 \(i\)\((t,1)\)\(SG\) 值。

  • \(g_{3,j,i}\) 表示 \((t,i)\)\(SG\) 值为 \(j\),棋子从 \(m\) 移动到 \(i\)\((t,m)\)\(SG\) 值。

于是就可以对每个点进行 \(O(1)\) 计算了。总时间复杂度 \(O(\sum nm)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e3+5;
int T,n,m,sg[maxn][maxn],f[2][maxn],g[4][4][maxn];
string s[maxn];
il int mex(int a=-1,int b=-1,int c=-1){
	for(int i=0;;i++){
		if(i!=a&&i!=b&&i!=c){
			return i;
		}
	}
}
il void solve1(int t){
	#define gt(i) ((i)>m?(i)-m:(i))
	memset(f,-1,sizeof(f));
	int _1=1;
	while(s[t][_1]!='#'){
		_1++;
	}
	for(int l=_1,r;l<m+_1;l=r){
		r=l+1;
		while(s[t][r]!='#'){
			r++;
		}
		if(r==l+1){
			continue;
		}
		f[0][l+1]=mex(sg[t+1][gt(l+1)]);
		for(int i=l+2;i<r;i++){
			f[0][i]=mex(f[0][i-1],sg[t+1][gt(i)]);
		}
		f[1][r-1]=mex(sg[t+1][gt(r-1)]);
		for(int i=r-2;i>l;i--){
			f[1][i]=mex(f[1][i+1],sg[t+1][gt(i)]);
		}
		for(int i=l+1;i<r;i++){
			sg[t][gt(i)]=mex(f[0][i-1],f[1][i+1],sg[t+1][gt(i)]);
		}
	}
	#undef gt
}
il void solve2(int t){
	if(m==1){
		sg[t][1]=mex(sg[t+1][1]);
		return ;
	}
	for(int i:{0,1,2,3}){
		g[0][i][1]=g[1][i][m]=g[2][i][1]=g[3][i][m]=i;
	}
	for(int i=2;i<=m;i++){
		for(int j:{0,1,2,3}){
			g[0][j][i]=mex(g[0][j][i-1],sg[t+1][i]);
		}
	}
	for(int i=m-1;i;i--){
		for(int j:{0,1,2,3}){
			g[1][j][i]=mex(g[1][j][i+1],sg[t+1][i]);
		}
	}
	for(int i=2;i<=m;i++){
		for(int j:{0,1,2,3}){
			g[2][j][i]=g[2][mex(j,sg[t+1][i-1])][i-1];
		}
	}
	for(int i=m-1;i;i--){
		for(int j:{0,1,2,3}){
			g[3][j][i]=g[3][mex(j,sg[t+1][i+1])][i+1];
		}
	}
	for(int i=1;i<=m;i++){
		int l,r;
		if(i<m){
			int tmp=g[3][mex(sg[t+1][i+1])][i+1];
			if(i==1){
				l=tmp;
			}
			else{
				l=g[0][mex(tmp,sg[t+1][1])][i-1];
			}
		}
		else{
			l=g[0][mex(sg[t+1][1])][i-1];
		}
		if(i>1){
			int tmp=g[2][mex(sg[t+1][i-1])][i-1];
			if(i==m){
				r=tmp;
			}
			else{
				r=g[1][mex(tmp,sg[t+1][m])][i+1];
			}
		}
		else{
			r=g[1][mex(sg[t+1][m])][i+1];
		}
		sg[t][i]=mex(l,r,sg[t+1][i]);
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			cin>>s[i];
			s[i]=" "+s[i]+s[i];
		}
		for(int i=1;i<=n+1;i++){
			for(int j=1;j<=m;j++){
				sg[i][j]=-1;
			}
		}
		for(int i=n;i;i--){
			for(int j=1;j<=m;j++){
				if(s[i][j]=='#'){
					solve1(i);
					goto togo;
				}
			}
			solve2(i);
			togo:;
		}
		int ans=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(s[i][j]=='B'){
					ans^=sg[i][j];
				}
			}
		}
		cout<<(ans?'A':'B')<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}

D. 公司

2025CSP-S模拟赛39

A B C D Sum Rank
100 0 30 20 150 10/19

A. poohfrog

直接 bfs 即可,时间复杂度 \(O(nmk)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=6e6+5,inf=1e9;
const int dx[]={-1,1,0,0,0,0};
const int dy[]={0,0,-1,1,0,0};
const int dz[]={0,0,0,0,-1,1};
int n,m,kk,xx,yy,zz,hd=1,tl=0,dep[185][185][185];
bool vis[185][185][185];
char s[185][185][185];
struct node{
	int x,y,z;
	node(int x=0,int y=0,int z=0):x(x),y(y),z(z){}
}q[maxn];
int main(){
//	freopen("ex_poohfrog.in","r",stdin);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m>>kk>>xx>>yy>>zz;
	for(int k=1;k<=kk;k++){
		for(int i=1;i<=n;i++){
			cin>>s[k][i]+1;
		}
	}
	memset(dep,0x3f,sizeof(dep));
	vis[xx][yy][zz]=1,dep[xx][yy][zz]=0;
	q[++tl]=node(xx,yy,zz);
	while(hd<=tl){
		node u=q[hd++];
		int x=u.x,y=u.y,z=u.z;
//		cout<<x<<' '<<y<<' '<<z<<'\n';
		for(int i=0;i<=5;i++){
			int nx=x+dx[i],ny=y+dy[i],nz=z+dz[i];
			if(nx&&nx<=n&&ny&&ny<=m&&nz&&nz<=kk&&!vis[nx][ny][nz]&&s[nz][nx][ny]=='.'){
				vis[nx][ny][nz]=1,dep[nx][ny][nz]=dep[x][y][z]+1;
				q[++tl]=node(nx,ny,nz);
			}
		}
	}
	int ans=inf;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			ans=min(ans,dep[i][j][kk]);
		}
	}
	cout<<ans*2;
//	cerr<<'\n'<<clock()*1.0;
	return 0;
}
}
int main(){return asbt::main();}

B. 数学题

C. 货物运输

D. 士兵游戏

posted @ 2025-08-15 17:07  zhangxy__hp  阅读(56)  评论(0)    收藏  举报