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

A. 皮胚 (match)

可行性 dp。设 \(f_{i,j}\) 表示 \(s\) 的第 \(i\) 位能否匹配到 \(t\) 的第 \(j\) 位。分讨转移即可。

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;
int T;
bool f[maxn][maxn];
string s,t;
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>s>>t;
		int ls=s.size(),lt=t.size();
		s=" "+s,t=" "+t;
		if(t[1]=='.'||s[1]==t[1]){
			memset(f,0,sizeof f);
			f[1][1]=1;
		}
		else{
			cout<<0<<"\n";
			continue;
		}
		for(int i=1;i<=ls;i++){
			for(int j=1;j<=lt;j++){
				if(!f[i][j]){
					continue;
				}
				if(t[j]=='*'&&s[i+1]==s[i]){
					f[i+1][j]=1;
				}
				if(t[j+1]=='.'){
					f[i+1][j+1]=1;
				}
				else if(t[j+1]=='*'){
					f[i][j+1]=1;
					if(s[i+1]==s[i]){
						f[i+1][j+1]=1;
					}
				}
				else if(s[i+1]==t[j+1]){
					f[i+1][j+1]=1;
				}
			}
		}
		int ans=0;
		for(int i=1;i<=ls;i++){
			ans+=f[i][lt];
		}
		cout<<ans<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}

B. 核冰 (merge)

考虑一个贪心暴力,从小到大枚举每一个数,如果一个数合并后还有剩余(即出现次数大于等于 \(3\)),那么就不断合并。

考虑用线段树去优化。用线段树维护合并到最后每个数出现的次数 \(cnt\)。考虑插入一个数 \(x\),可能会引起一系列的合并,即从 \(x\) 开始的连续一段 \(cnt=2\) 的数都可以合并进位上去。遇到 \(cnt<2\) 的数时停止。线段树二分即可。

考虑删除一个数 \(x\),类似地,需要连续地借位。这时需要满足的有两个条件,分别是当前数出现次数为 \(1\),以及当前数进行过进位。于是再开一棵线段树维护每个数的进位次数。

Code
#include<bits/stdc++.h>
#define ll 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=5e5+25,N=5e5+20;
int n,m,a[maxn],pos[maxn];
il void gpos(int id,int l,int r){
	if(l==r){
		pos[l]=id;
		return ;
	}
	int mid=(l+r)>>1;
	gpos(lid,l,mid);
	gpos(rid,mid+1,r);
}
struct{
	int mx[maxn<<2],mn[maxn<<2],tag[maxn<<2];
	il void pushup(int id){
		mx[id]=max(mx[lid],mx[rid]);
		mn[id]=min(mn[lid],mn[rid]);
	}
	il void pushtag(int id,int v){
		mx[id]+=v,mn[id]+=v,tag[id]+=v;
	}
	il void pushdown(int id){
		if(tag[id]){
			pushtag(lid,tag[id]);
			pushtag(rid,tag[id]);
			tag[id]=0;
		}
	}
	il void add(int id,int L,int R,int l,int r,int v){
		if(l>r){
			return ;
		}
		if(L>=l&&R<=r){
			pushtag(id,v);
			return ;
		}
		pushdown(id);
		int mid=(L+R)>>1;
		if(l<=mid){
			add(lid,L,mid,l,r,v);
		}
		if(r>mid){
			add(rid,mid+1,R,l,r,v);
		}
		pushup(id);
	}
	il int ef1(int id,int L,int R,int l,int r){
		if(R<l||L>r){
			return -1;
		}
		if(mn[id]>=2){
			return -1;
		}
		if(L==R){
			return L;
		}
		pushdown(id);
		int mid=(L+R)>>1;
		int res=ef1(lid,L,mid,l,r);
		if(res==-1){
			res=ef1(rid,mid+1,R,l,r);
		}
		return res;
	}
	il int ef2(int id,int L,int R,int l,int r){
		if(R<l||L>r){
			return -1;
		}
		if(mx[id]<=1){
			return -1;
		}
		if(L==R){
			return L;
		}
		pushdown(id);
		int mid=(L+R)>>1;
		int res=ef2(lid,L,mid,l,r);
		if(res==-1){
			res=ef2(rid,mid+1,R,l,r);
		}
		return res;
	}
}S1;
struct{
	int mn[maxn<<2],tag[maxn<<2];
	il void pushup(int id){
		mn[id]=min(mn[lid],mn[rid]);
	}
	il void pushtag(int id,int v){
		mn[id]+=v,tag[id]+=v;
	}
	il void pushdown(int id){
		if(tag[id]){
			pushtag(lid,tag[id]);
			pushtag(rid,tag[id]);
			tag[id]=0;
		}
	}
	il void add(int id,int L,int R,int l,int r,int v){
		if(l>r){
			return ;
		}
		if(L>=l&&R<=r){
			pushtag(id,v);
			return ;
		}
		pushdown(id);
		int mid=(L+R)>>1;
		if(l<=mid){
			add(lid,L,mid,l,r,v);
		}
		if(r>mid){
			add(rid,mid+1,R,l,r,v);
		}
		pushup(id);
	}
	il int ef(int id,int L,int R,int l,int r){
//		cout<<id<<" "<<L<<" "<<R<<" "<<l<<" "<<r<<"\n";
		if(mn[id]>=1){
			return -1;
		}
		if(R<l||L>r){
			return -1;
		}
		if(L==R){
			return L;
		}
		pushdown(id);
		int mid=(L+R)>>1;
		int res=ef(lid,L,mid,l,r);
		if(res==-1){
			res=ef(rid,mid+1,R,l,r);
		}
		return res;
	}
}S2;
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	gpos(1,1,N);
	cin>>n>>m;
	int ans=0;
	function<void(int)> insert=[&](int x){
		int p=S1.ef1(1,1,N,x,N);
//		cout<<p<<"\n";
		if(!S1.mx[pos[p]]){
			ans++;
		}
		S1.add(1,1,N,x,p-1,-1);
		S1.add(1,1,N,p,p,1);
		S2.add(1,1,N,x,p-1,1);
	};
	function<void(int)> del=[&](int x){
		int p=S1.ef2(1,1,N,x,N),q=S2.ef(1,1,N,x,N);
//		cout<<p<<" "<<q<<"\n";
		if(p==-1||~q&&p>q){
			p=q;
		}
		if(S1.mx[pos[p]]==1){
			ans--;
		}
		S1.add(1,1,N,x,p-1,1);
		S1.add(1,1,N,p,p,-1);
		S2.add(1,1,N,x,p-1,-1);
	};
	for(int i=1;i<=n;i++){
		cin>>a[i];
		insert(a[i]);
	}
//	cout<<ans<<"\n";
	while(m--){
		int opt;
		cin>>opt;
		if(opt==1){
			int p,v;
			cin>>p>>v;
			del(a[p]);
			a[p]=v;
			insert(a[p]);
		}
		else{
			cout<<ans<<"\n";
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

C. 方珍 (mex)

显然瓶颈在于如何快速求出第 \(k\) 大的 \(\operatorname{mex}\)。考虑二分。设当前答案为 \(x\),那么可以双指针求出 \(\operatorname{mex}<x\) 的区间数量。于是获得了 \(O(n^2\log n)\) 的算法。

考虑一个神秘优化。首先将 \(w\) 降序排序,动态地维护答案。设当前答案为 \(ans\),那么我们就不断尝试 \(ans+1-w\) 是否合法即可。由于 \(w\) 降序,\(ans\) 最多增加 \(O(n)\) 次。因此时间复杂度为 \(O(n^2)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ull unsigned ll
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e4+5;
int n,k[maxn],w[maxn],m[maxn],a[maxn],p[maxn],ton[maxn];
ull rs[maxn];
il int rd(int i){
	rs[i]^=rs[i]<<13;
	rs[i]^=rs[i]>>7;
	rs[i]^=rs[i]<<17;
	return rs[i]%m[i];
}
il int check(int x){
//	cout<<x<<"\n";
	for(int i=0;i<=n;i++){
		ton[i]=0;
	}
	int res=0,cnt=0;
	for(int l=1,r=1;r<=n;r++){
		if(a[r]<x){
			cnt+=ton[a[r]]==0;
			ton[a[r]]++;
		}
		while(cnt>=x){
			while(a[l]>=x){
				l++;
			}
			ton[a[l]]--;
			cnt-=ton[a[l]]==0;
			l++;
		}
		res+=r-l+1;
	}
	return res;
}
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>>k[i]>>w[i]>>m[i]>>rs[i];
		p[i]=i;
	}
//	puts("666");
	sort(p+1,p+n+1,[](const int &x,const int &y){return w[x]>w[y];});
	int ans=w[p[1]];
	for(int i=1;i<=n;i++){
//		cout<<i<<":\n";
		int x=p[i];
		if(ans>=m[x]+w[x]){
			continue;
		}
		for(int j=1;j<=n;j++){
			a[j]=rd(x);
		}
		while(check(ans+1-w[x])<k[x]){
			ans++;
		}
	}
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}

D. 术劣 (sequence)

有这样一个神奇的推论:等差序列 \(a\) 打乱后公差 \(d=\gcd(|a_2-a_1|,|a_3-a_2|,\dots,|a_n-a_{n-1}|)\)。对此搞笑椅子 AI 给出了严谨的证明过程。[1]

由此,我们可以进一步推知:

  1. 对于任意序列 \(a\)\(\max\{a\}-\min\{a\}\ge (n-1)d\)

  2. 当且仅当 \(a\) 排序后是等差序列时,上式取等。

于是对于题目中 \(a\) 序列的一个区间 \([l,r]\),我们就可以通过判断 \(\max[l,r]-\min[l,r]=(r-l)d\) 是否成立即可。移项,得 \(\max[l,r]-\min[l,r]+ld=rd\)。由是我们可以考虑线段树经典套路:移动右端点 \(r\),在每个位置 \(x\) 维护 \([x,r]\) 的信息。这里我们维护的就是 \(\max[x,r]-\min[x,r]+xd\)

考虑 \(r\) 右移时应该怎么在线段树上修改。其中 \(\max\)\(\min\) 可以用单调栈简单地完成。对于 \(d\) 的改变,显然从 \(1\)\(r-1\)\(d\) 是不降的,且连续颜色段是 \(O(\log)\) 级别的。于是我们用并查集将连续段合并起来,在需要修改时直接暴力修改。这样时间正确的原因是,显然 \(d\) 最多也就改变 \(O(\log)\) 次。时间复杂度 \(O(n\log^2)\)

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define gcd __gcd
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=2e5+5;
int n,a[maxn],st1[maxn],tp1,st2[maxn],tp2;
struct node{
	int mn,num;
	node(int mn=0,int num=0):mn(mn),num(num){}
	il node operator+(const node &x)const{
		if(mn==x.mn){
			return node(mn,num+x.num);
		}
		else if(mn<x.mn){
			return *this;
		}
		else{
			return x;
		}
	}
}tr[maxn<<2];
int tag[maxn<<2];
#define lid id<<1
#define rid id<<1|1
#define mn(id) tr[id].mn
#define num(id) tr[id].num
il void pushup(int id){
	tr[id]=tr[lid]+tr[rid];
}
il void pushtag(int id,int v){
	tag[id]+=v,mn(id)+=v;
}
il void pushdown(int id){
	if(tag[id]){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=0;
	}
}
il void build(int id,int l,int r){
	if(l==r){
		tr[id]=node(0,1);
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
il void add(int id,int L,int R,int l,int r,int v){
	if(L>=l&&R<=r){
		pushtag(id,v);
		return ;
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(l<=mid){
		add(lid,L,mid,l,r,v);
	}
	if(r>mid){
		add(rid,mid+1,R,l,r,v);
	}
	pushup(id);
}
il node query(int id,int L,int R,int l,int r){
	if(L>=l&&R<=r){
		return tr[id];
	}
	pushdown(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);
}
int fa[maxn],lp[maxn],d[maxn];
il int find(int x){
	return x!=fa[x]?fa[x]=find(fa[x]):x;
}
//il void debug(int id,int l,int r){
//	cerr<<id<<" "<<l<<" "<<r<<" "<<mn(id)<<" "<<num(id)<<"\n";
//	if(l==r){
//		return ;
//	}
//	int mid=(l+r)>>1;
//	debug(lid,l,mid);
//	debug(rid,mid+1,r);
//}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
//	freopen("D.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		fa[i]=lp[i]=i;
	}
	build(1,1,n);
//	debug(1,1,n);
	int ans=1;
	for(int r=1;r<=n;r++){
//		cerr<<r<<":\n";
		while(tp1&&a[st1[tp1]]>=a[r]){
			add(1,1,n,st1[tp1-1]+1,st1[tp1],a[st1[tp1]]-a[r]);
			tp1--;
		}
		st1[++tp1]=r;
		while(tp2&&a[st2[tp2]]<=a[r]){
			add(1,1,n,st2[tp2-1]+1,st2[tp2],a[r]-a[st2[tp2]]);
			tp2--;
		}
		st2[++tp2]=r;
		if(r==1){
			cout<<1<<" ";
			continue;
		}
		int cur=abs(a[r]-a[r-1]);
		d[r-1]=cur;
//		cerr<<cur<<" "<<r<<"\n";
////		puts("666");
//		debug(1,1,n);
//		cerr<<"---------------------------------\n";
		add(1,1,n,r-1,r-1,cur*(r-1));
//		debug(1,1,n);
//		puts("777");
		for(int i=r-2,j=r-1;i;i--){
			i=find(i);
			cur=gcd(cur,d[i]);
			if(cur!=d[i]){
				for(int k=lp[i];k<j;k++){
					add(1,1,n,k,k,k*(cur-d[i]));
				}
				d[i]=cur;
			}
			if(d[i]==d[find(j)]){
				int x=find(i),y=find(j);
				fa[x]=y;
				lp[y]=min(lp[x],lp[y]);
			}
			i=j=lp[find(i)];
		}
//		cout<<ans<<"\n";
		ans++;
		for(int i=r-1,j=r;i;i--){
			i=find(i);
			node res=query(1,1,n,lp[i],j-1);
			if(res.mn==r*d[i]){
				ans+=res.num;
			}
			i=j=lp[find(i)];
		}
//		cout<<ans<<"\n";
		cout<<ans<<" ";
	}
	return 0;
}
}
signed main(){return asbt::main();}

  1. 设等差数列为 \(a,a+d,a+2d,\dots,a+(n-1)d\)。那么对于乱序后的两个相邻的数 \(a+pd\)\(a+qd\),二者之差即为 \(|p-q|d\)。若存在 \(k\in\mathbb{Z}\)\(k>1\),使得所有的 \(kd\) 整除 \(|p-q|d\),即 \(k\mid p-q\),那么所有的 \(p\)\(q\) 都将存在于 \(k\) 的某一个剩余系中,又 \(p,q\in[0,n-1]\) 且两两不等,那 \(p,q\) 就不够了。因此这样的 \(k\) 是不存在的。 ↩︎

posted @ 2025-06-01 19:17  zhangxy__hp  阅读(63)  评论(23)    收藏  举报