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

A B C D Sum Rank
100 - 30 10 140 6/20

今天使用了表格😜

A. 马

新题的数据很水,可能有许多正确率较高的贪心能够通过。

正解是 \(O(m^3)\) 的 DP,然而我写的是 \(O(nm^3)\) 的(

\(f_{i,x,y,z}\) 表示前 \(i\) 匹马总共 \(x\) 次大圈,\(y\) 次小圈,\(z\) 次过河,前 \(i-1\) 匹马的疲劳值都 \(\le 100\),第 \(i\) 匹马的最小疲惫值。转移时可以从上一匹马转,也可以当前这匹马转。需要滚动数组。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int inf=1e9;
int n,m,a,b,c,f[2][305][305][305],ans;
il void dfs(int x,int ra,int rb,int rc){
	if(x>n){
//		if(m-ra-rb-rc==9){
//			cout<<"---------------------------------------------\n";
//			cout<<hp[1][1]<<" "<<hp[1][2]<<" "<<hp[1][3]<<"\n";
//			cout<<hp[2][1]<<" "<<hp[2][2]<<" "<<hp[2][3]<<"\n";
//			cout<<hp[3][1]<<" "<<hp[3][2]<<" "<<hp[3][3]<<"\n";
//			cout<<"---------------------------------------------\n";
//		}
		ans=max(ans,m-ra-rb-rc);
		return ;
	}
	for(int i=0;i<=ra;i++){
		for(int j=0;j<=rb;j++){
			for(int k=0;k<=rc;k++){
				if(i*50+j*20+(1<<k)<=100){
//					hp[x][1]=i,hp[x][2]=j,hp[x][3]=k;
					dfs(x+1,ra-i,rb-j,rc-k);
				}
			}
		}
	}
}
il void upd(int &x,int y){
	x=x<y?x:y;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m>>a>>b>>c;
	if(n<=10&&m<=10){
		dfs(1,a,b,c);
		cout<<ans;
		return 0;
	}
	for(int x=0;x<=a;x++){
		for(int y=0;y<=b;y++){
			for(int z=0;z<=c;z++){
				f[0][x][y][z]=inf;
			}
		}
	}
	f[0][0][0][0]=1;
	for(int i=1,j=1;i<=n;i++,j^=1){
		for(int x=0;x<=a;x++){
			for(int y=0;y<=b;y++){
				for(int z=0;z<=c;z++){
					f[j][x][y][z]=inf;
				}
			}
		}
		f[j][0][0][0]=1;
		for(int x=0;x<=a;x++){
			for(int y=0;y<=b;y++){
				for(int z=0;z<=c;z++){
					int &res=f[j][x][y][z];
					if(f[j^1][x][y][z]<=100){
						res=1;
						continue;
					}
					if(x){
						if(f[j^1][x-1][y][z]<=100){
							upd(res,51);
						}
						else{
							upd(res,min(f[j][x-1][y][z]+50,inf));
						}
					}
					if(y){
						if(f[j^1][x][y-1][z]<=100){
							upd(res,21);
						}
						else{
							upd(res,min(f[j][x][y-1][z]+20,inf));
						}
					}
					if(z){
						if(f[j^1][x][y][z-1]<=100){
							upd(res,2);
						}
						else{
							upd(res,min(f[j][x][y][z-1]<<1,inf));
						}
					}
				}
			}
		}
	}
	for(int x=0;x<=a;x++){
		for(int y=0;y<=b;y++){
			for(int z=0;z<=c;z++){
				if(f[n&1][x][y][z]<=100){
					ans=max(ans,x+y+z);
				}
			}
		}
	}
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}

B. 可爱捏

大体思路是,将每个数分解质因数,去掉所有立方因子后能组成完全立方数的数是一一对应的,在两坨中去更大的一坨就好了。瓶颈在于分解质因数。

考虑根号分治。首先分出所有 \(\le\sqrt[3]{a_i}\) 的质因数,剩下的就是两个质数相乘了。使用欧拉筛,每次分解质因数的时间复杂度都是 \(O(\sqrt[3]{a_i})\) 的。

Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define fir first
#define sec second
using namespace std;
namespace asbt{
const int maxn=3e3+5;
int n,ans,prn,prm[maxn],hp1[15],hp2[15];
bool npr[maxn];
map<__int128,int> cnt;
map<__int128,__int128> ying;
il void euler(int x=3e3){
	for(int i=2;i<=x;i++){
		if(!npr[i]){
			prm[++prn]=i;
		}
		for(int j=1;j<=prn&&i*prm[j]<=x;j++){
			npr[i*prm[j]]=1;
			if(i%prm[j]==0){
				break;
			}
		}
	}
}
il int qpow(int x,int y){
	return y==0?1:y==1?x:x*x;
}
int main(){
//	freopen("lovely07.in","r",stdin);
	ios::sync_with_stdio(0),cin.tie(0);
	euler();
	cin>>n;
	int ans=0;
	while(n--){
		int x,tot=0;
		cin>>x;
//		cout<<x<<"\n";
		for(int i=1;i<=prn;i++){
			if(x%prm[i]==0){
				hp1[++tot]=prm[i];
				while(x%prm[i]==0){
					x/=prm[i];
					hp2[tot]++;
				}
			}
		}
		for(int i=1;i<=prn;i++){
			if(x%prm[i]==0){
				x/=i;
				if(x==i){
					hp1[++tot]=i;
					hp2[tot]=2;
				}
				else{
					hp1[++tot]=i;
					hp2[tot]=1;
					hp1[++tot]=x;
					hp2[tot]=1;
				}
				goto togo;
			}
		}
		if(x!=1){
			hp1[++tot]=x;
			hp2[tot]=1;
		}
		togo:;
//		cout<<tot<<"\n";
//		for(int i=1;i<=tot;i++){
//			cout<<hp1[i]<<" "<<hp2[i]<<"\n";
//		}
		__int128 a=1,b=1;
		for(int i=1;i<=tot;i++){
			hp2[i]%=3;
			a*=qpow(hp1[i],hp2[i]);
			b*=qpow(hp1[i],(3-hp2[i])%3);
		}
		if(a==1){
			if(!ans){
				ans++;
			}
		}
		else{
			cnt[a]++;
			ying[a]=b,ying[b]=a;
		}
		for(int i=1;i<=tot;i++){
			hp1[i]=hp2[i]=0;
		}
	}
//	cerr<<clock()<<"\n";
	for(auto i:ying){
		__int128 x=i.fir,y=i.sec;
		ans+=max(cnt[x],cnt[y]);
	}
	cout<<(ans+1)/2;
	return 0;
}
}
signed main(){return asbt::main();}

C. 诗

根号分治*2。

考虑对于所有长度 \(\le B\) 的字符串,暴力预处理出文本串中的所有可能的子串,二分查找;长度 \(>B\) 的字符串,直接在文本串中暴力查找。据传 \(B=100\) 时最优。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ull unsigned ll
#define lwrb lower_bound
#define uprb upper_bound
using namespace std;
namespace asbt{
const int maxn=1e5+5,B=100;
const ull bas1=1e6+151;
int opt,n,m,a[maxn],b[maxn],cnt[105];
ull pw1[maxn],ha1[maxn],hp[105][maxn];
il ull gha1(int l,int r){
	return ha1[r]-ha1[l-1]*pw1[r-l+1];
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>opt>>n>>m;
	pw1[0]=1;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		pw1[i]=pw1[i-1]*bas1;
		ha1[i]=ha1[i-1]*bas1+a[i];
	}
	for(int i=1;i<=B;i++){
		for(int l=1,r=i;r<=n;l++,r++){
			hp[i][++cnt[i]]=gha1(l,r);
		}
		sort(hp[i]+1,hp[i]+cnt[i]+1);
	}
	int ans=0;
	while(m--){
		int k;
		ull hb1=0;
		cin>>k;
		for(int i=1;i<=k;i++){
			cin>>b[i];
			if(opt){
				b[i]^=ans;
			}
			hb1=hb1*bas1+b[i];
		}
		ans=0;
		if(k<=B){
			ans=uprb(hp[k]+1,hp[k]+cnt[k]+1,hb1)-lwrb(hp[k]+1,hp[k]+cnt[k]+1,hb1);
		}
		else{
			for(int l=1,r=k;r<=n;l++,r++){
				if(hb1==gha1(l,r)){
					ans++;
				}
			}
		}
		cout<<ans<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}

D. 相似

首先有一个转化:将 \(G_i\) 改为 \(G_i\)\(S_i\) 中的位置,于是要求变为一段区间值连续,即 \(max-min+1=len\)

对于 \(k=1\) 的部分分(30pts),考虑移动右端点,用线段树维护 \([i,r]\)\(max-min-len\)。其中 \(len\) 是好维护的,\(max\)\(min\) 可以用单调栈维护。

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{
const int maxn=2e5+5;
int n,m,a[maxn],b[maxn],pos[maxn];
int st1[maxn],tp1,st2[maxn],tp2,tag[maxn<<2];
struct node{
	int val,num;
	node(int val=0,int num=1):val(val),num(num){}
	il node operator+(const node &x)const{
		if(val<x.val){
			return *this;
		}
		if(val>x.val){
			return x;
		}
		return node(val,num+x.num);
	}
}tr[maxn<<2];
il void pushup(int id){
	tr[id]=tr[lid]+tr[rid];
}
il void pushtag(int id,int v){
	tag[id]+=v,tr[id].val+=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>=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 main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		pos[a[i]]=i;
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
		b[i]=pos[b[i]];
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
		add(1,1,n,1,i,-1);
		while(tp1&&b[st1[tp1]]<=b[i]){
			add(1,1,n,st1[tp1-1]+1,st1[tp1],b[i]-b[st1[tp1]]);
			tp1--;
		}
		while(tp2&&b[st2[tp2]]>=b[i]){
			add(1,1,n,st2[tp2-1]+1,st2[tp2],b[st2[tp2]]-b[i]);
			tp2--;
		}
		st1[++tp1]=st2[++tp2]=i;
		ans+=query(1,1,n,1,i).num;
	}
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2025-07-08 16:59  zhangxy__hp  阅读(37)  评论(0)    收藏  举报