2025.8.9 CSP-S模拟赛31

小长假回来后的第一场模拟赛

159pts,没啥技术含量,就是暴力打满(挂了10pts

T1 花海

赛时只打了50分暴力;

这种类型的题目以前基本没遇见过,但是思路很重要;

观察到数据范围,直接暴力矩阵甚至会爆空间,观察到 \(n*m \leq 2*10^5\) ,拿吗我们假设其中 \(n<m\) ,那么有 \(n \leq \sqrt{2*10^5}\) ;时限有3s,于是我们可以考虑到 \(O(n^2m)\) 做法;

借用cql大佬的思路,我们枚举两行作为矩阵上下界,然后自左向右枚举矩阵的左边界并将其记为 \(mxl_i\),同时统计以 \(i\) 为起始向右拓展的最大矩阵边界值;因为前缀和优化可以 \(O(1)\) 求出矩阵值,所以上述操作可以 \(O(m)\) 完成;

可以看下图,基本就是这样统计,重叠部分抵消:

image

还有不懂得可以看代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10,MAXM=5e2+10;
int n,m,a[MAXM][MAXN],qzh[MAXM][MAXN];
int maxn;
vector<int> ls;
int mxl[MAXN],mxr[MAXN];
int get_(int xf,int yf,int xs,int ys){
	return qzh[xs][ys]-qzh[xf-1][ys]-qzh[xs][yf-1]+qzh[xf-1][yf-1];
}
int main(){
	freopen("flower.in","r",stdin);
	freopen("flower.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	if(n<m){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin>>a[i][j];
			}
		}
	}
	else{
		for(int i=1;i<=n*m;i++){
			int x;cin>>x;
			ls.push_back(x);
		}
		swap(n,m);
		for(int i=1;i<=n;i++){
			for(int p=1,j=0;p<=m;p++,j+=n){
				a[i][p]=ls[i+j-1];
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			qzh[i][j]=a[i][j]+qzh[i-1][j]+qzh[i][j-1]-qzh[i-1][j-1];
		}
	}
	for(int l=1;l<n;l++){
		for(int r=l+1;r<=n;r++){
			int sum=0;
			for(int i=1;i<=m;i++) mxl[i]=get_(l+1,i,r-1,i)-sum,sum+=a[l][i]+a[r][i];
			sum=0;
			for(int i=m;i>=1;i--) mxr[i]=get_(l+1,i,r-1,i)-sum,sum+=a[l][i]+a[r][i];
			for(int i=m-1;i;i--) mxr[i]=max(mxr[i],mxr[i+1]);
			for(int i=1;i<m;i++) maxn=max(maxn,sum+mxl[i]+mxr[i+1]);
		}
	}
	cout<<maxn;
	return 0;
} 

T2 划分

\(f_i\)表示\(1-i\)的划分的最大值和最小值

\[f_i=Min f_{j-1}+Max_{k=j}^{i} a_k,\sum_{k=j}^{i}a_k\le m \]

然后考虑后面部分可以用一个单调队列维护,然后就完了;

代码如下:

#include<bits/stdc++.h>
using namespace std;
long long n,m,a[100005],f[100005][20],qz[100005];
long long minn[100005];
long long lx=0,rx,up_line,ans=1e18,maxn;
long long dp[100005];
unsigned long long pf=13331,ps=131;
long long que[100005],head=1,teil,xb=0;
int rmq_query(int l,int r){
	int k=log2(r-l+1);
	return max(f[l][k],f[r-(1<<k)+1][k]);
}
int main(){
	freopen("split.in","r",stdin);
	freopen("split.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	minn[0]=1e18;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		qz[i]=qz[i-1]+a[i];
		f[i][0]=a[i];
	}
	for(int j=1;(1<<j)<=n;++j){
		for(int i=1;i+(1<<j)-1<=n;++i){
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
		}
	}
	memset(dp,0x3f3f3f3f,sizeof dp);
	dp[0]=0;
	for(int i=1;i<=n;i++){
		while(qz[i]-qz[xb]>m) xb++;
		while(head<=teil&&que[head]<=xb) head++;
		while(head<=teil&&a[que[teil]]<a[i]) teil--;
		que[++teil]=i;
		dp[i]=min(dp[i],dp[xb]+a[que[head]]);
		for(int j=head;j<teil;j++){
			dp[i]=min(dp[i],dp[que[j]]+a[que[j+1]]);
		}
	}
	cout<<dp[n];
	return 0; 
}

T3 落子无悔

使用zhangxy大佬的思路,题解直接看就行
image

当然主播不会用set,所以用优先队列打标记完成,代码如下:

#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,fa[maxn],p[maxn],a[maxn];
int _0[maxn],_1[maxn],nxt[maxn],lst[maxn];
il int find(int x){
	return x!=p[x]?p[x]=find(p[x]):x;
}
struct node{
	int u,_0,_1;
	node(int u=0,int _0=0,int _1=0):u(u),_0(_0),_1(_1){}
	il bool operator<(const node &x)const{
		if(_0*x._1==x._0*_1){
			return u<x.u;
		}
		return _0*x._1>x._0*_1;
	}
};
set<node> S;
int main(){
	freopen("board.in","r",stdin);
	freopen("board.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=2;i<=n;i++){
		cin>>fa[i];
	}
	for(int i=1;i<=n;i++){
		cin>>a[i];
		_0[i]=a[i]^1,_1[i]=a[i];
		p[i]=i,nxt[i]=lst[i]=i;
	}
	for(int i=2;i<=n;i++){
		S.insert(node(i,_0[i],_1[i]));
	}
	for(int i=1;i<n;i++){
		int u=S.begin()->u,v=find(fa[u]);
		S.erase(node(u,_0[u],_1[u]));
		if(v!=1){
			S.erase(node(v,_0[v],_1[v]));
		}
		_0[v]+=_0[u],_1[v]+=_1[u];
		nxt[lst[v]]=u,lst[v]=lst[u];
		p[u]=v;
		if(v!=1){
			S.insert(node(v,_0[v],_1[v]));
		}
	}
	ll cnt=0,ans=0;
	for(int i=1;;i=nxt[i]){
		if(!a[i]){
			ans+=cnt;
		}
		else{
			cnt++;
		}
		if(i==nxt[i]){
			break;
		}
	}
	cout<<ans;
	return 0;
}
}
int main(){return asbt::main();}

T4 体测

30%

对于 $n \leq 6 $ ,\(l,r \leq 10\) 的情况,直接爆搜即可;

50%

对于 \(a_i=1\) 的情况,直接因为单个项目一天只能满足一个,直接判断多个约束条件的总上界减总下界是否足够 \(n\) ,否则就无解;

100%

什么?真的吗?你听说了吗?我真的会吗?

直接粘题解吧:

如果每个右端点不一样,可以通过每次取右端点构造一个方案

反之

我们来构造一个右端点不一样的区间

对右端点从大到小排序,得到{r1,r2,……},对于相同的r,可以将他们变成{r,r-1,r-2……},这便得到了最优的右端点序列

一个很显然的想法,让左端点较小的区间有较小的右端点显然更优

然后开始分配右端点,按左端点从大到小排序后,每次取离r最近且<=r的右端点,顺便判Sorry

这可以用set实现;

显然一定存在一个答案最小的区间每次选择的点都是右端点(调整法)

之后按右端点从小到大选择必须的

每次找出左端点小于等于当前右端点的全部区间,按不同的种类删去右端点最小的区间,可以用set维护

时间复杂度为 \(O(nlgn)\)

posted @ 2025-08-13 11:55  zhangch_qwq  阅读(20)  评论(0)    收藏  举报