2025模拟赛10

T1 T2 T3 T4 T5 T6
\({\color{#F39C11} 普及− }\) \({\color{#FFC116} 普及/提高− }\) \({\color{#52C41A} 普及+/提高 }\) \({\color{#52C41A} 普及+/提高 }\) \({\color{#3498DB} 提高+/省选− }\) \({\color{#3498DB} 提高+/省选− }\)

参赛网址:https://boyacoding.cn/contest

T1 茳峤串【2025暑假集训T1】

题目传送门

题目难度:\({\color{#F39C11} 普及− }\)

算法标签:搜索,枚举,模拟

思路

我不准备说什么了,直接看代码吧

题目强化版

我们通过字符串的 \(01(10)\) 串的数量排序。

  • \(cnt=0\) 时,即原字符串全 \(0\) 或全 \(1\) 时,我们变成最基础的茳峤串需要 \(2\) 的代价。

  • \(cnt=1\) 时,即 \(0 \dots 01 \dots 1\)(或相反)时,只需要 \(1\) 次即可。

    ::::error[然后...]

    你就挂了

    \(hack:0011\)

    我们发现在这种情况下($$n=4$$)必须要 \(2\) 次才可以。

    ::::

  • \(cnt=2\) 时,即 \(0 \dots 01 \dots 10 \dots 0\)(或相反)时,只需要 \(1\) 次即可。

    :::error[然后...]

    你就挂了

    \(hack:1001\)

    我们发现在这种情况下(\(n=4\) 并且 \(1\) 的数量和 \(0\) 的数量一样)只要 \(1\) 次。
    :::

  • \(cnt \ge 3\) \(continue\)

AC Code

#include <bits/stdc++.h>
using namespace std;

int T,n;
string s;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>T;
	while (T--){
		int cnt=0;
		cin>>n>>s;
		s='.'+s;
		int cnt0=0,cnt1=0;
		cnt0+=(s[n]=='0');
		cnt1+=(s[n]=='1');
		for (int i=1;i<n;i++){
			cnt0+=(s[i]=='0');
			cnt1+=(s[i]=='1');
			if (s[i]!=s[i+1])
				cnt++;
		}
		if (cnt==0)	cout<<2<<"\n";
		else if (cnt==1){
			if (n==4){
				if (cnt0==1)	cout<<1<<"\n";
				else if (cnt1==1)	cout<<1<<"\n";
				else	cout<<2<<"\n";
			}
			else	cout<<1<<"\n";
		}
		else if (cnt==2){
			if (n==4){
				if (cnt0==cnt1)	cout<<2<<"\n";
				else	cout<<1<<"\n";
			}
			else	cout<<1<<"\n";
		}
		else	cout<<0<<"\n";
	}
	return 0;
}

T2 构造01串【2025暑假集训T2】

题目传送门

题目难度:\({\color{#FFC116} 普及/提高− }\)

算法标签:动态规划,背包

思路

首先我们知道:

\(1\dots 1\) \(n\)\(1\) 时,他的贡献是 \(\frac {n*(n+1)} 2\)

则就是用 \(num\) 个 $ \frac {i*(i+1)} 2 $ 的和 \(=k\) 使 \(num\) 最小 。

然后考虑 \(f_i\) 表示对于 \(i\) 的答案。

转移是因为每个部分都是形如 \(\frac {i*(i+1)} 2\),所以 \(f_i\) 肯定是由 \(f_{i-j*(j+1)/2}(i-j*(j+1)/2 \ge 0)\) 转移来的,转移的代价是 \(j+1\),即 \(j\) 长度的 \(1\)\(1\) 长度的 \(0\)

$f[i]=\min f[i-j(j+1)/2]+j+1 (\forall j 有\frac {j(j+1)} 2) $

然后记录一下每次转移的 \(j\) 递归求答案即可。

AC Code

#include <bits/stdc++.h>
using namespace std;

const int maxn=2e5;
const int inf=1e9+7;
int T;
int n;
int f[maxn+5],w[maxn+5];
string ans;

int check(int x){
	int d=sqrt(8*x+1);
	int y=(d-1)/2;
	if (y*(y+1)/2==x)	return y;
	return 0;
}

void print(int x){
	if (x==0)	return ;
	if (x==1){
		ans=ans+'1';
		ans=ans+'0';
		return ;
	}
	if (check(x)){
		for (int i=1;i<=check(x);i++)	ans=ans+'1';
		ans=ans+'0';
		return ;
	}
	for (int i=1;i<=w[x];i++)	ans=ans+'1';
	ans=ans+'0';
	print(x-w[x]*(w[x]+1)/2);
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>T;
	for (int i=1;i<=maxn;i++)	f[i]=inf;
	for (int i=1;i<=maxn;i++){
		if (check(i)){
			f[i]=check(i)+1;
			w[i]=0;
			continue;
		}
		for (int j=1;j*(j+1)/2<=i;j++){
			if (f[i]>f[i-j*(j+1)/2]+j+1){
				f[i]=f[i-j*(j+1)/2]+j+1;
				w[i]=j;
			}
		}
	}
	while (T--){
		cin>>n;
		ans="";
		print(n);
		for (int i=0;i<(int)ans.size()-1;i++)	cout<<ans[i];
		cout<<"\n";
	}
	return 0;
}

T3 树上拼好数【2025暑假集训T3】

题目传送门

题目难度:\({\color{#52C41A} 普及+/提高 }\)

算法标签:搜索,枚举,动态规划,树上DP

思路

因为 \(\text{lca(u,v)=u or lca(u,v)=v}\) 所以 \(\text{(u,v)}\) 就是一条链。

对于此题,直接爆搜,~但我场上没想到QAQ QVQ QWQ~

对于每一个点,考虑对于他的子树找到小于距离他小于 \(20\) 的点,对于这条链,从上往下跑和从下往上的两种情况分别考虑,注意 \(1\) 个点的情况只算一次。

AC Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn=2e5+5;
int n,Q;
int f[maxn];
int M[(2<<21)+1];
string s;
vector<int> G[maxn],e[maxn];

void dfs(int u,int fa){
	f[u]=fa;
	for (int i=0;i<G[u].size();i++){
		int v=G[u][i];
		if (v==fa)	continue;
		dfs(v,u);
	}
}

void dfs_sum(int u,int fa,int val,int tot,int dep){
	if (dep>20)	return ;
	val=(val<<1)|(s[u]-'0');
	tot=tot|((s[u]-'0')<<dep);
	M[val]++;
	M[tot]++;
	for (int i=0;i<G[u].size();i++){
		int v=G[u][i];
		if (v==fa)	continue;
		dfs_sum(v,u,val,tot,dep+1);
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>Q;
	cin>>s;
	s='.'+s;
	for (int i=1;i<=n-1;i++){
		int u,v;
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0);
	for (int i=1;i<=n;i++){
		dfs_sum(i,f[i],0,0,0);
		M[(s[i]-'0')]--;
	}
	while (Q--){
		int x;
		cin>>x;
		cout<<M[x]<<"\n";
	}
	return 0;
}

T4 复杂问题的不平衡性【2025暑假集训T4】

题目传送门

题目难度:\({\color{#52C41A} 普及+/提高 }\)

算法标签:其他,双指针扫描,二分查找,排序

思路

对于此题,考虑暴力。

贪心的发现应该将难度差的最大值位置分开才有用,否则不可能对答案进行更改。

60pts

#include <bits/stdc++.h>
using namespace std;

const int maxn=2e5+5;
int T;
int n,m,k;
int a[maxn],b[maxn],c[maxn];
int s[maxn];
vector<int> d;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>T;
	while (T--){
		int ans=1e9;
		cin>>n>>m>>k;
		int Max=0,sMax=0;
		int idMax=0,idsMax=0;
		for (int i=1;i<=n;i++)	cin>>a[i];
		for (int i=1;i<=m;i++)	cin>>b[i];
		for (int i=1;i<=k;i++)	cin>>c[i];
		sort(a+1,a+n+1);
		for (int i=2;i<=n;i++){
			s[i]=a[i]-a[i-1];
			if (Max<=s[i]){
				sMax=Max;
				idsMax=idMax;
				Max=s[i];
				idMax=i;
			}
			else if (sMax<s[i]){
				idsMax=i;
				sMax=s[i];
			}
		}
		ans=Max;
		for (int i=1;i<=m;i++)
			for (int j=1;j<=k;j++)
				d.push_back(b[i]+c[j]);
		sort(d.begin(),d.end());
		int t=lower_bound(d.begin(),d.end(),a[idMax-1])-d.begin();
		int t1=lower_bound(d.begin(),d.end(),a[idMax])-d.begin();
		for (int i=t;i<=t1-1;i++){
			int f=d[i];
			ans=min(ans,max(sMax,max(a[idMax]-f,f-a[idMax-1])));
		}
		cout<<ans<<"\n";
		d.clear();
	}
	return 0;
}

然后考虑优化:

首先将代码从:

for (int i=1;i<=m;i++)
			for (int j=1;j<=k;j++)
				d.push_back(b[i]+c[j]);
		sort(d.begin(),d.end());
		int t=lower_bound(d.begin(),d.end(),a[idMax-1])-d.begin();
		int t1=lower_bound(d.begin(),d.end(),a[idMax])-d.begin();
		for (int i=t;i<=t1-1;i++){
			int f=d[i];
			ans=min(ans,max(sMax,max(a[idMax]-f,f-a[idMax-1])));
		}

改为

for (int i=1;i<=m;i++)
		for (int j=1;j<=k;j++){
			int f=b[i]+c[j];
			if (a[idMax-1]<=f&&f<=a[idMax])	ans=min(ans,max(sMax,max(a[idMax]-f,f-a[idMax-1])));
		}

然后发现,我们寻找的是:

当给定 \(i\) ,使得 \(a_{idMax-1} \le b_i+c_j\) 并且 \(b_i+c_j \le a_{idMax}\)

\(a_{idMax-1}-c_j \le b_i\) 并且 \(b_i \le a_{idMax}-c_j\)

二分查找即可。

AC Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn=2e5+5;
int T;
int n,m,k;
int a[maxn],b[maxn],c[maxn];
int s[maxn];

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>T;
	while (T--){
		int ans=1e9;
		cin>>n>>m>>k;
		int Max=0,sMax=0;
		int idMax=0;
		for (int i=1;i<=n;i++)	cin>>a[i];
		for (int i=1;i<=m;i++)	cin>>b[i];
		for (int i=1;i<=k;i++)	cin>>c[i];
		sort(a+1,a+n+1);
		sort(b+1,b+m+1);
		sort(c+1,c+k+1);
		for (int i=2;i<=n;i++){
			s[i]=a[i]-a[i-1];
			if (Max<=s[i]){
				sMax=Max;
				Max=s[i];
				idMax=i;
			}
			else if (sMax<s[i])
				sMax=s[i];
		}
		ans=Max;
		for (int i=1;i<=m;i++){
			int t=lower_bound(c+1,c+k+1,a[idMax-1]-b[i])-c;
			int t1=upper_bound(c+1,c+k+1,a[idMax]-b[i])-c-1;
			for (int j=t;j<=t1;j++){
				int f=b[i]+c[j];
				if (a[idMax-1]<=f&&f<=a[idMax])	ans=min(ans,max(sMax,max(a[idMax]-f,f-a[idMax-1])));
			}
		}
		cout<<ans<<"\n";
	}
	return 0;
}

T5 最大中位数【2025暑假集训T5】

题目传送门

题目难度:\({\color{#3498DB} 提高+/省选− }\)

算法标签:其他,二分查找,动态规划,贪心

思路

来源: CF 1993D

链接:Problem - 1993D - Codeforces

详见:https://boyacoding.cn/p/G0113/solution/68954d5aa18c3feebb7e6eaf

对于此题,考虑二分答案。

我们知道最后留下的数应该是 \((n-1)\mod k+1\) 个。

所以我们考虑使用 动态规划 进行二分的 \(\text {check}\)

\(dp_j\)

\(dp_j=max(dp_j-1+(a_i \ge mid),dp_j);\)

AC Code

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn=5e5+5;
int T;
int n,k;
int res;
int a[maxn],dp[maxn];

bool check(int mid){
	for (int i=0;i<=k;i++)	dp[i]=0;
	for (int i=1;i<=n;i++){
		int j=(i-1)%k+1;
		dp[j]=max(dp[j-1]+(a[i]>=mid),dp[j]);
	}
	if (dp[res]>res/2)	return 1;
	return 0;
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>T;
	while (T--){
		cin>>n>>k;
		res=(n-1)%k+1;
		for (int i=1;i<=n;i++)	cin>>a[i];
		if (n<=k){
			sort(a+1,a+n+1);
			cout<<a[(n+1)/2]<<"\n";
			continue;
		}
		int l=1,r=1e9,ans=0;
		while (l<=r){
			int mid=(l+r)>>1;
			if (check(mid)){
				ans=mid;
				l=mid+1;
			}
			else	r=mid-1;
		}
		cout<<ans<<"\n";
	}
	return 0;
}

T6 花开遍地【2025暑假集训T6】

题目传送门

题目难度:\({\color{#3498DB} 提高+/省选− }\)

算法标签:搜索,记忆化搜索,枚举,模拟

思路

我们发现,对于一个连通块,设它的上边缘为 \(u\),下边缘为 \(d\),左边缘为 \(l\),右边缘为 \(r\),给定 \(i,j\) 位置放药水,当且仅当 \(i \in [u-1,d+1]\) , \(j \in [l-1,r+1]\),时可以连接此连通块。

我们又知道在 \((i,j)\) 使用药水后该点所在的连通块大小变成了 \(n+m-1 + 连接起来的连通块大小之和 - 这一行的开花数量 - 这一列的开花数量 + (i,j)这个点开花了\)

所以预处理。

::::warning[警告]
这是一个警告框。

hack

7 7 1
.......
.......
..*****
..*****
..*****
..*****
..*****
1 1

原因读者自证不难

::::

AC Code

#include <bits/stdc++.h>
using namespace std;

const int maxn=3005;
int n,m,Q;
char mp[maxn][maxn];

int col;
int vis[maxn][maxn];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
queue<pair<int,int> > P;

int Max;
int ans[maxn][maxn];
int s[maxn];
int d_h[maxn],d_l[maxn];
int num_h[maxn],num_l[maxn];
struct ST{int l,r,val;};
vector<ST> G[maxn];

void bfs(int sx,int sy){
	int u=sx,d=sx,l=sy,r=sy;
	int cnt=1;
	P.push({sx,sy});
	while (P.size()>0){
		pair<int,int> t=P.front();
		P.pop();
		int x=t.first,y=t.second;
		u=min(x,u);
		d=max(x,d);
		l=min(y,l);
		r=max(y,r);
		for (int i=0;i<=3;i++){
			int tx=x+dx[i];
			int ty=y+dy[i];
			if (x>=1&&x<=n&&y>=1&&y<=m)
				if (vis[tx][ty]==0&&mp[tx][ty]=='*'){
					P.push({tx,ty});
					vis[tx][ty]=col;
					cnt++;
				}
		}
	}
	Max=max(Max,cnt);
	u--;d++;
	l--;r++;
	d_h[u]+=cnt;
	d_h[d+1]-=cnt;
	d_l[l]+=cnt;
	d_l[r+1]-=cnt;
	G[u].push_back({l,r+1,-cnt});
	G[d+1].push_back({l,r+1,cnt});
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>Q;
	for (int i=1;i<=n;i++)
	for (int j=1;j<=m;j++){
		cin>>mp[i][j];
		if (mp[i][j]=='*'){
			num_h[i]++;
			num_l[j]++;
		}
	}
	for (int i=1;i<=n;i++)
	for (int j=1;j<=m;j++)
		if (vis[i][j]==0&&mp[i][j]=='*'){
			vis[i][j]=++col;
			bfs(i,j);
		}
	for (int i=0;i<=n;i++){
		if (i!=0)	d_h[i]+=d_h[i-1];
		for (int j=0;j<G[i].size();j++){
			ST t=G[i][j];
			d_l[t.l]+=t.val;
			d_l[t.r]-=t.val;
		}
		s[0]=d_l[0];
		for (int j=1;j<=m;j++){
			s[j]=s[j-1]+d_l[j];
			if (i!=0){
				ans[i][j]=n+m-1+d_h[i]+s[j]-num_h[i]-num_l[j];
				if (mp[i][j]=='*')ans[i][j]++;
				ans[i][j]=max(ans[i][j],Max);
			}
		}
	}
	while (Q--){
		int x,y;
		cin>>x>>y;
		cout<<ans[x][y]<<"\n";
	}
}
posted @ 2025-09-27 21:39  Zzqyoung1121  阅读(10)  评论(0)    收藏  举报