专栏文章
题解:P11361 [NOIP2024] 编辑字符串
P11361题解参与者 4已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 3 条
- 当前快照
- 1 份
- 快照标识符
- @miqxi6n3
- 此快照首次捕获于
- 2025/12/04 12:21 3 个月前
- 此快照最后确认于
- 2025/12/04 12:21 3 个月前
题意理解:
- 交换相邻字符:连续的 内 位置可以任意调换顺序,连续的 内 位置可以任意调换顺序。
- 目标:使对应位置字符尽可能相等。
思路:
已知 与 只有以下三种对应情况:
- 时,此时 与 字符已经锁死,因此只需要比较对应位置字符是否相同即可,如果相同就使 。
- 时,此时 与 都不确定,下面会讨论。
- , 或 , 时,有一个字符确定,要尽量使另一个字符去迎合锁定字符,下面再进行讨论。
现在问题变成了可移动字符应该如何移动?
由于连续的可移动字符可以任意调换顺序,因此可以将连续的可移动字符看作一块。
因此原序列就变成了若干个可调换顺序的字符块与锁定字符的序列。在划分块的时需要记录在当前位置 的字符属于哪个可移动块,并且记录当前块有多少个 和 。
接下来再考虑如何调换。
首先考虑 , 或 , 的情况。我们只需要考虑可移动的块内是否拥有当前不可移动的字符。如果有,则使当前可移动块的对应字符个数减 ,并使 ,相当于将这个字符填在了这个位置。
接着考虑 的情况,此时这个位置都在可移动块内,只需分别比对对应块的 的个数以及 的个数,如果都有 或者都有 ,就在这个位置填上 或者 ,并使 ,两个可移动块对应字符个数分别减 。
至此,问题已经解决,请看代码实现:
CPP#include<bits/stdc++.h>
using namespace std;
int t,n;
char a[1145140],b[1145140],c[1145140],d[1145140];
int loca[1145140],locb[1145140];//a[i]与b[i]当前位置为第几块
int blocka[1145140][3],blockb[1145140][3];//记录第某一块的字符0的总数与1的总数
int main(){
freopen("edit.in","r",stdin);
freopen("edit.out","w",stdout);
cin>>t;
while(t--){
int ans=0;//记录答案
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++)cin>>c[i];
for(int i=1;i<=n;i++)cin>>d[i];
//输入
int cnta=1;//cnta记录当前为第几块
for(int i=1;i<=n;i++){
if(c[i]=='1'){
loca[i]=cnta;//记录当前位置属于哪个块
blocka[cnta][a[i]-'0']++;//块内对应字符个数+1
}
if(c[i]=='0'&&c[i-1]=='1'){
cnta++;//此块已经结束,开一个新块
}
}
int cntb=1;//b[i]与a[i]同理进行处理
for(int i=1;i<=n;i++){
if(d[i]=='1'){
locb[i]=cntb;
blockb[cntb][b[i]-'0']++;
}
if(d[i]=='0'&&d[i-1]=='1'){
cntb++;
}
}
for(int i=1;i<=n;i++)if(c[i]=='0'&&d[i]=='0'&&a[i]==b[i])ans++;//当两位置锁定且相同时ans++
for(int i=1;i<=n;i++){
if(c[i]=='1'&&d[i]=='0'){
if(blocka[loca[i]][b[i]-'0']!=0){//若当前块存在与锁定字符相同的字符时,将此字符填入
blocka[loca[i]][b[i]-'0']--;
ans++;
}
}
if(c[i]=='0'&&d[i]=='1'){
if(blockb[locb[i]][a[i]-'0']!=0){//同理
blockb[locb[i]][a[i]-'0']--;
ans++;
}
}
}
for(int i=1;i<=n;i++){
if(c[i]=='1'&&d[i]=='1'){
if(blocka[loca[i]][1]!=0&&blockb[locb[i]][1]!=0){
blocka[loca[i]][1]--;
blockb[locb[i]][1]--;
ans++;
}
else if(blocka[loca[i]][0]!=0&&blockb[locb[i]][0]!=0){
blocka[loca[i]][0]--;
blockb[locb[i]][0]--;
ans++;
}
}
}
cout<<ans<<endl;
for(int i=1;i<=n;i++)loca[i]=0,locb[i]=0;
//多测不清空,亲人两行泪
for(int i=1;i<=cnta;i++)blocka[i][0]=0,blocka[i][1]=0;
//多测不清空,亲人两行泪
for(int i=1;i<=cntb;i++)blockb[i][0]=0,blockb[i][1]=0;
//多测不清空,亲人两行泪
//重要的事情说三遍
}
return 0;
}
这可能是真正意义上的第一次场切蓝题,但这也可能是最后一场比赛了,珍惜和 OI 在一起的时光吧,继续走下去。
相关推荐
评论
共 3 条评论,欢迎与作者交流。
正在加载评论...