题解 CF1370E Binary Subsequence Rotation
Posted wxy-max
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 CF1370E Binary Subsequence Rotation相关的知识,希望对你有一定的参考价值。
题目大意
题目描述
给定两个长度为(n)的(01)串(a)和(b),要求串(a)的任意子序列经过若干次“旋转”操作变为串(b)
对于一次“旋转操作”我们这样定义:
如果我们要旋转的序列为
(c_1,c_2,c_3,c_4,c_5...c_n)
那么旋转之后的序列为(c_n,c_1,c_2,c_3,c_4...c_{n-1})
例如对于(s=1110100)
如果我们旋转的子序列的下标为({2,6,7})(从(1)开始)
那么旋转之后的串为$1010110 $
求至少进行多少次“旋转”操作,能够把串(a)变成串(b)
输入格式
输入一共三行
第一行输入一个整数(n(1≤n≤10^6))
第二行输入串(a)
第三行输入串(b)
输出格式
输出为(1)行,为最小的操作次数
思路
首先,如果要求我们改变的次数最小,那么我们就只改变(a)与(b)中不相同的元素。
设集合(c)为(a)中与(b)中的元素不同的元素,并且对于每个元素其值等于(a)的值。
而对于(c)中的元素而言,每一对不同的(1)和(0)都可以进行一次“旋转”操作,我们可以看作其两个元素进行交换。
而若干个元素的“旋转”操作可以看作若干次“交换”操作
因此如果(c)当中的(1)和(0)的个数不同是一定不能将(a)变为串(b)
而现在,我们的目标为用尽量少的操作使得(c)中的元素全部取反(即(1)变成(0),(0)变成(1))
那么我们每次进行旋转操作的序列一定是形如(010101)或(101010)这样的序列(必须为偶数位)
如果我们进行旋转操作的序列为形如(111000)这样的序列,那么就等价于我们进行三次(10)这样的旋转操作
那么我们要使得进行的操作次数尽量少,即每次使得我们选的子序列尽量的长
那么我们每进行一次操作就会使得和最大的子段的和减一,即每进行一次操作都能使得(max(sum_{i=l}^rleft | c_i ight |))减一
证明:
假设子段([l,r])的和为正数,那么子段两边的元素一定是(-1),否则(sum_{i=l}^rleft | c_i ight |)可以更大。
同理(c[l])与(c[r])必然是(1),否则缩小区间范围,一定可以更大。
设该子段中一共有(k)个(1),长度为(m),那么该子段的(-1)的个数为(m-k)。
那么在该子段外至少有(2 imes k -m)个(-1)
对于其中的(-1),我们至多(m-k)次就可以将所有的(-1)变(1)(即所有的(-1)全都挨在一起的情况)
而对于每次操作,我们选择的(1)的个数比(-1)的个数多一定更优
因为我们当前子段的和是最大的,且为正,因此(1)的个数必然比(-1)的个数多
考虑反证
如果我们选的(-1)比(1)多的话,那么在当前子段至多选
(2 imes m-2 imes k-1)个元素
如果我们(1)的个数比(-1)的个数多,那么当前子段至多选
(2 imes m-2 imes k+1)个元素
而对于外面的元素,如果排序方式改变最多损失两个元素
因此至少不会变坏,且会更优
如果我们(-1)选(p)个,那么(1)必然会选(p+1)个
那么,其减少了(1)
如果到最后的序列不存在(-1),那么每次我们至少可以从当前序列中选出一个(1)取反,因为在该子段外至少有(2 imes k -m)个(-1)
因此,每次操作我们都可以使得(max(sum_{i=l}^rleft | c_i ight |))减一
并且,因为其和最大,所以能够确保每次操作,都能够作用于当前子段
那么,当(max(sum_{i=l}^rleft | c_i ight |))减为(0)时,即最小操作次数。
答案即为(max(sum_{i=l}^rleft | c_i ight |))
(QED)
而我们可以用下面代码(O(n))的求出上述式子
#include <iostream>
using namespace std;
const int maxn = 1e6 + 10;
char a[maxn],b[maxn];
int c[maxn],n;
int get(int x){
int cur = 0,res = 0;
for (int i = 0; i < n; i++){
cur += x * c[i];
res = max(res,cur);
if (cur < 0) cur = 0;
}
return res;
}
int main(){
cin >> n >> a >> b;
int s0 = 0,s1 = 0;
for (int i = 0; i < n; i++){
if (a[i] != b[i] && a[i] == ‘1‘){
c[i] = 1;
s1++;
}
if (a[i] != b[i] && a[i] == ‘0‘){
c[i] = -1;
s0++;
}
}
if (s1 != s0){
cout << -1;
//system("PAUSE");
return 0;
}
cout << max(get(1),get(-1));
//system("PAUSE");
return 0;
}
以上是关于题解 CF1370E Binary Subsequence Rotation的主要内容,如果未能解决你的问题,请参考以下文章