Luogu P11361 NOIP2024 编辑字符串 题解 [ 蓝 ] [ 贪心 ]
编辑字符串:adhoc 贪心题。
结论
找到两个字符串种连续的且可以移动的所有极大子区间,然后线性扫一遍,看这一位所处的子区间中有多少个 \(0\) 和 \(1\),两个都有 \(0\) 就先消 \(0\),否则如果两个都有 \(1\) 就把 \(1\) 消掉,就做完了。
时间 \(O(Tn)\)。
证明
洛谷上有一种很妙的证明,借鉴一下。
这题的答案显然是:
\[n-\sum_{i=1}^{n}(a_i-b_i)^2=n-(\sum_{i=1}^{n}(a_i^2+b_i^2-2a_ib_i))
\]
\[=n-(\sum_{i=1}^{n}a_i^2+\sum_{i=1}^{n}b_i^2-\sum_{i=1}^{n}2a_ib_i)
\]
\[=n-\sum_{i=1}^{n}a_i^2-\sum_{i=1}^{n}b_i^2+\sum_{i=1}^{n}2a_ib_i
\]
显然 \(n\) 与 \(\sum_{i=1}^{n}a_i^2\) 与 \(\sum_{i=1}^{n}b_i^2\) 已经是定值,那么我们要最大化 \(\sum_{i=1}^{n}2a_ib_i\),就要把 \(a_i,b_i\) 先全部放 \(1\)。
由于这题 \(0\) 与 \(1\) 具有对称性,所以先全部放 \(0\) 也是对的。
代码
代码也不难写,考场上 10min 打完的,思路也就想了 10min,还包括手模样例的时间。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,cnta,cntb,bla[100005],blb[100005],tota[100005][2],totb[100005][2],ans;
bitset<100005>a,b,ta,tb;
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
char x;
cin>>x;
a[i]=(x-'0');
}
for(int i=1;i<=n;i++)
{
char x;
cin>>x;
b[i]=(x-'0');
}
cnta=0;
for(int i=1;i<=n;i++)
{
char x;
cin>>x;
ta[i]=(x-'0');
if(ta[i]!=ta[i-1]||ta[i]==0)cnta++;
bla[i]=cnta;
}
cntb=0;
for(int i=1;i<=n;i++)
{
char x;
cin>>x;
tb[i]=(x-'0');
if(tb[i]!=tb[i-1]||tb[i]==0)cntb++;
blb[i]=cntb;
}
memset(tota,0,sizeof(tota));
memset(totb,0,sizeof(totb));
for(int i=1;i<=n;i++)
{
tota[bla[i]][a[i]]++;
totb[blb[i]][b[i]]++;
}
ans=0;
for(int i=1;i<=n;i++)
{
if(tota[bla[i]][0]>0&&totb[blb[i]][0]>0)
{
ans++;
tota[bla[i]][0]--;
totb[blb[i]][0]--;
}
else if(tota[bla[i]][1]>0&&totb[blb[i]][1]>0)
{
ans++;
tota[bla[i]][1]--;
totb[blb[i]][1]--;
}
}
cout<<ans<<'\n';
}
int main()
{
freopen("edit.in","r",stdin);
freopen("edit.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)solve();
return 0;
}

浙公网安备 33010602011771号