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

A B C D Sum Rank
- 25 20 75 120 19/25

乱序放题,被 T1 硬控了啊啊啊啊啊

A. 铁轨

考虑图论。连边:\(s_i\xrightarrow{0}t_i,v\xrightarrow{1}v-1\)。但是 \(\le s_i\) 这个限制十分难搞,考虑把它强制变为 \(=s_i\),再连边 \(v\xrightarrow{0}v+1\)。再加一条铁轨 \((+\infty,1)\),于是我们只需求一条包含了所有 \(s_i\to t_i\) 的欧拉回路即可。

因为所有点都在数轴上,所以所有 \(v\to v+1\)\(v+1\to v\) 的经过次数应是相等的。首先差分出对每个 \(v\),所有 \(s_i\to t_i\) 经过 \(v\to v+1\) 的次数与经过 \(v+1\to v\) 的次数之差。若不为 \(0\),则需要一定量的 \(v\xrightarrow{1}v-1\)\(v\xrightarrow{0}v+1\) 来补齐。

此时我们将图连成了若干连通块,不一定构成回路,又需要一些 \(v\to v+1\)\(v+1\to v\) 来连通。于是缩点跑 kruskal 即可。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lwrb lower_bound
using namespace std;
namespace asbt{
const int maxn=4e5+5,inf=1e18;
int n,a[maxn],b[maxn],lsh[maxn],tot,c[maxn],fa[maxn],cnt;
il int find(int x){
	return x!=fa[x]?fa[x]=find(fa[x]):x;
}
struct edge{
	int u,v,w;
	il bool operator<(const edge &x)const{
		return w<x.w;
	}
}e[maxn];
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];
		lsh[++tot]=a[i];
		lsh[++tot]=b[i];
	}
	a[++n]=inf,b[n]=1;
	lsh[++tot]=1,lsh[++tot]=inf;
	sort(lsh+1,lsh+tot+1);
	tot=unique(lsh+1,lsh+tot+1)-lsh-1;
	for(int i=1;i<=tot;i++){
		fa[i]=i;
	}
	for(int i=1;i<=n;i++){
		a[i]=lwrb(lsh+1,lsh+tot+1,a[i])-lsh;
		b[i]=lwrb(lsh+1,lsh+tot+1,b[i])-lsh;
		c[a[i]]++,c[b[i]]--;
		fa[find(a[i])]=find(b[i]);
	}
	int ans=0;
	for(int i=1;i<=tot;i++){
		c[i]+=c[i-1];
		if(c[i]){
			fa[find(i)]=find(i+1);
			if(c[i]>0){
				ans+=(lsh[i+1]-lsh[i])*c[i];
			}
		}
	}
	for(int i=1;i<tot;i++){
		int u=find(i),v=find(i+1);
		if(u!=v){
			e[++cnt]=(edge){u,v,lsh[i+1]-lsh[i]};
		}
	}
	sort(e+1,e+cnt+1);
	for(int i=1;i<=cnt;i++){
		int u=find(e[i].u),v=find(e[i].v);
		if(u!=v){
			ans+=e[i].w,fa[u]=v;
		}
	}
	cout<<ans;
	return 0;
}
}
signed main(){return asbt::main();}

B. 参加

看到区间操作想到差分,设差分数组为 \(b\),那么要求即为 \(\forall i\in[1,k],b_i>0,\forall i\in[k+1,n],b_i<0\)。最小操作数即为前后的 \(\max\)

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e5+5,inf=1e18;
int n,a[maxn];
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int pre=0,suf=0;
	for(int i=n;i>1;i--){
		a[i]-=a[i-1];
		if(a[i]>=0){
			suf+=a[i]+1;
		}
	}
//	for(int i=1;i<=n;i++){
//		cout<<a[i]<<' ';
//	}
//	cout<<'\n';
	int ans=inf;
	for(int i=1;i<=n;i++){
//		cout<<i<<' '<<pre<<' '<<suf<<'\n';
		ans=min(ans,max(pre,suf));
		if(a[i+1]<0){
			pre-=a[i+1]-1;
		}else if(a[i+1]>0){
			suf-=a[i+1]+1;
		}else{
			pre++,suf--;
		}
	}
	cout<<ans;
	return 0;
}
}
signed main(){return asbt::main();}

C. 决斗

首先考虑求出最大得分,考虑权值线段树,合并时用左区间的 \(A\) 数量和右区间的 \(B\) 数量贡献即可。考虑构造解,对每一位二分,考察取掉这个数后能否在线段树上凑出答案即可。时间复杂度线性对数方。

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{
const int maxn=1e5+5;
int n,a[maxn],b[maxn];
int tr[maxn<<2][2],sum[maxn<<2],sz[maxn<<2];
il void pushup(int id){
	sum[id]=sum[lid]+sum[rid]+min(tr[lid][0],tr[rid][1]);
	tr[id][0]=max(tr[lid][0]-tr[rid][1],0ll)+tr[rid][0];
	tr[id][1]=tr[lid][1]+max(tr[rid][1]-tr[lid][0],0ll);
	sz[id]=sz[lid]+sz[rid];
}
il void add(int id,int l,int r,int p,bool opt,int v){
	if(l==r){
		tr[id][opt]+=v;
		if(opt){
			sz[id]+=v;
		}
		return ;
	}
	int mid=(l+r)>>1;
	if(p<=mid){
		add(lid,l,mid,p,opt,v);
	}else{
		add(rid,mid+1,r,p,opt,v);
	}
	pushup(id);
}
il int rank(int id,int l,int r,int p){
	if(l==r){
		return sz[id];
	}
	int mid=(l+r)>>1;
	if(p<=mid){
		return rank(lid,l,mid,p);
	}else{
		return sz[lid]+rank(rid,mid+1,r,p);
	}
}
il int kth(int id,int l,int r,int k){
	if(l==r){
		return l;
	}
	int mid=(l+r)>>1;
	if(sz[lid]>=k){
		return kth(lid,l,mid,k);
	}else{
		return kth(rid,mid+1,r,k-sz[lid]);
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		add(1,1,1e5,a[i],0,1);
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
		add(1,1,1e5,b[i],1,1);
	}
	int tar=sum[1],cur=0;
	for(int i=1;i<=n;i++){
		add(1,1,1e5,a[i],0,-1);
		int c=rank(1,1,1e5,a[i]);
		int l=c+1,r=sz[1],res=0;
		while(l<=r){
			int mid=(l+r)>>1;
			int t=kth(1,1,1e5,mid);
			add(1,1,1e5,t,1,-1);
			if(sum[1]+1+cur==tar){
				l=mid+1,res=mid;
			}else{
				r=mid-1;
			}
			add(1,1,1e5,t,1,1);
		}
		if(!res){
			l=1,r=c;
			while(l<=r){
				int mid=(l+r)>>1;
				int t=kth(1,1,1e5,mid);
				add(1,1,1e5,t,1,-1);
				if(sum[1]+cur==tar){
					l=mid+1,res=mid;
				}else{
					r=mid-1;
				}
				add(1,1,1e5,t,1,1);
			}
		}
		res=kth(1,1,1e5,res);
		add(1,1,1e5,res,1,-1);
		cur+=(res>a[i]);
//		cout<<res<<'\n';
		cout<<res<<' ';
	}
	return 0;
}
}
signed main(){return asbt::main();}

D. 回文串问题

发现只要长度相同那么一定不会出现 Not equal。考虑并查集,并对字符串进行哈希。时间复杂度瓶颈有两个:合并和维护哈希值。

考虑最终顶多将所有点都合并成一个点,也就是说暴力合并的话有许多合并操作都是多余的。考虑不断二分出 lcp 后对下一个位置进行合并,于是顶多合并 \(O(n)\) 次。而哈希值,可以用树状数组维护。但是我们每次合并,哈希值要改变的位置是有很多的,启发式合并即可。时间复杂度线性对数方。

Code
#include<bits/stdc++.h>
#define ull unsigned long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e5+5;
const ull bas=1e5+393;
int n,m,fa[maxn];
ull pw[maxn];
set<int> s[maxn];
struct{
	#define lowbit(x) (x&-x)
	ull tr[maxn];
	il void add(int p,ull v){
		for(;p<=n;p+=lowbit(p)){
			tr[p]+=v;
		}
	}
	il ull query(int p){
		ull res=0;
		for(;p;p-=lowbit(p)){
			res+=tr[p];
		}
		return res;
	}
	#undef lowbit
}F1,F2;
il int find(int x){
	return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void merge(int u,int v){
	u=find(u),v=find(v);
	if(u==v){
		return ;
	}
	if(s[u].size()>s[v].size()){
		swap(u,v);
	}
	fa[u]=v;
	for(int x:s[u]){
		s[v].insert(x);
		F1.add(x,(v-u)*pw[x]);
		F2.add(n-x+1,(v-u)*pw[n-x+1]);
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	pw[0]=1;
	for(int i=1;i<=n;i++){
		fa[i]=i,s[i].insert(i);
		pw[i]=pw[i-1]*bas;
		F1.add(i,i*pw[i]);
		F2.add(i,(n-i+1)*pw[i]);
	}
	while(m--){
		int opt,l,r,x,y;
		cin>>opt>>l>>r;
		if(opt==1){
//			puts("666");
			int ll=1,rr=(r-l+1)>>1;
			while(ll<=rr){
				while(ll<rr){
					int mid=(ll+rr)>>1;
					ull hl=F1.query(l+mid-1)-F1.query(l-1);
					ull hr=F2.query(n-r+mid)-F2.query(n-r);
					if(hl*pw[n-r+1]==hr*pw[l]){
						ll=mid+1;
					}else{
						rr=mid;
					}
				}
//				cout<<l+ll-1<<' '<<r-ll+1<<'\n';
				merge(l+ll-1,r-ll+1);
				ll++,rr=(r-l+1)>>1;
			}
		}else{
			cin>>x>>y;
			if(r-l!=y-x){
				cout<<"Not equal\n";
			}else{
				ull hl=F1.query(r)-F1.query(l-1);
				ull hx=F1.query(y)-F1.query(x-1);
				cout<<(hl*pw[x]==hx*pw[l]?"Equal":"Unknown")<<'\n';
			}
		}
	}
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2025-10-04 21:38  zhangxy__hp  阅读(13)  评论(0)    收藏  举报