[NOIP2024] 编辑字符串
依次打开折叠框,提示由浅入深,希望对大家有用。提示框是提示,成功框是提示的答案。
没思路:
:::info 想想什么策略可以匹配的最多。 :::
:::success 显然能匹配尽量匹配。 :::
只有不成熟的想法(上面的内容):
:::info 注意到有一些固定的位置。好像和不固定的位置形成某种关系?观察一下具体怎么处理。 :::
:::success 不能动的位置把可以移动的位置分成了若干段。 :::
:::info 不能移动的位置怎么当分割点? :::
:::success 单独组成一段即可。 :::
成功分段的看这里:
:::info 考虑两个段怎么处理? :::
:::success
你注意到能匹配尽量匹配,比如说这两个段
证明:如果这里不匹配那到后面匹配贡献不变。
这里给一个参考代码。
#include<bits/stdc++.h>
using namespace std;
int T,n,ans;
string s1,s2,t1,t2;
vector<tuple<int,int,int,int> >seg1,seg2;
void Main(){
seg1.clear(),seg2.clear();
cin>>n>>s1>>s2>>t1>>t2;
s1=" "+s1,s2=" "+s2,t1=" "+t1,t2=" "+t2;
ans=0;
for(int i=1,cnt10=0,cnt11=0,cnt20=0,cnt21=0,lst1=1,lst2=1;i<=n+1;i++){
if(i==n+1){
if(lst1!=n+1)
seg1.push_back(make_tuple(lst1,n,cnt10,cnt11));
if(lst2!=n+1)
seg2.push_back(make_tuple(lst2,n,cnt20,cnt21));
break;
}
if(t1[i]=='0'){
if(lst1!=i)
seg1.push_back(make_tuple(lst1,i-1,cnt10,cnt11));
seg1.push_back(make_tuple(i,i,s1[i]=='0',s1[i]=='1'));
cnt10=cnt11=0,lst1=i+1;
}
else
cnt10+=(s1[i]=='0'),cnt11+=(s1[i]=='1');
if(t2[i]=='0'){
if(lst2!=i)
seg2.push_back(make_tuple(lst2,i-1,cnt20,cnt21));
seg2.push_back(make_tuple(i,i,s2[i]=='0',s2[i]=='1'));
cnt20=cnt21=0,lst2=i+1;
}
else
cnt20+=(s2[i]=='0'),cnt21+=(s2[i]=='1');
}// 分段
for(int i=0,j=0;i<seg1.size()&&j<seg2.size();){
auto&[l1,r1,cnt10,cnt11]=seg1[i];
auto&[l2,r2,cnt20,cnt21]=seg2[j];
ans+=min(cnt10,cnt20)+min(cnt11,cnt21);//尽量匹配
if(r1<=r2){
cnt20-=min(cnt10,cnt20),cnt21-=min(cnt11,cnt21);
i++;
}
if(r1>=r2){
cnt10-=min(cnt10,cnt20),cnt11-=min(cnt11,cnt21);
j++;
}//后续更新剩余
}
cout<<ans<<'\n';
return;
}
int main(){
for(cin>>T;T;--T)
Main();
return 0;
}
:::
:::warning
有问题,只有
思考有什么问题。问题在下面一个框。 :::
:::error
注意到我们只考虑尽量匹配更多的数,但是对于位置区间出现
答案在下面。 :::
::::success
我们进行观察,显然不能一个段包含另一个(不然公共部分长度短于短的段,短的段的长度就是公共部分),不相交更不用考虑贡献。显然是
:::info
考虑怎么匹配?匹配
:::success 其实你被诈骗了。
你注意到左边的段剩余的比公共部分长,说明前面有位置没匹配上。前面没匹配上有什么条件?至少
证明:考虑反证法,如果这个段有
所以不需要考虑到底匹配哪个,匹配
这也是这篇题解在这种情况遇到没匹配的直接匹配就是正确的的原因。
我不知道我是不是讲复杂了,应该没有吧。 :::
::::
于是你切了,发现代码不长。正解代码稍微改改即可,不在给了。
存档,如果觉得有帮助可以在这个专栏点赞,不知道能不能全站推荐。