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";
}
}