上海月赛5月月赛乙组T2题解
题目:
题目描述
Bob 有一个 01 串 ( S ),定义其一个子串 ([l, r]) 的权重为:
[
\max\left(\sum_{i \in [l, r]} [S_i = 0], \sum_{i \notin [l, r]} [S_i = 1]\right)
]
也就是子串中 0 的个数与子串外 1 的个数的较大值。
请求出所有子串的最小权重。在本题中,子串可以为空,其对应权重即为 ( S ) 中 1 的个数。
输入格式
第一行一个整数 ( T ) 表示数据组数,对于每组数据:
一行一个 01 串 ( S )。
输出格式
对于每组数据,输出一行一个整数表示答案。
数据范围
对于 30% 的数据,(\sum |S| \leq 100)。
对于 60% 的数据,(\sum |S| \leq 5000)。
对于 100% 的数据,(1 \leq T \leq 10^4),(1 \leq |S|, \sum |S| \leq 2 \times 10^5)。
样例数据
输入:
3
0011100
0101010
1101011
输出:
0
1
2
说明
对于第一组数据,子串 ( S[3,5] = "111" ) 的权重为 0。
对于第二组数据,子串 ( S[2,4] = "101" ) 的权重为 1。
对于第三组数据,子串 ( S[1,5] = "11010" ) 的权重为 2。
解法:
我们先假设 \(a = \sum_{i \in [l,r]}[S_i = 0],b = \sum_{i \not\in [l,r]}[S_i = 1]\)。
首先可以枚举 \(l\),然后你会发现 \(r\) 越大,\(b\) 只有可能不变或变小,而 \(a\) 只有可能不变或变大,然后 \(a\) 和 \(b\) 最接近是最好的,然后你会发现具有单调性,可以二分,然后分 \(a \ge b\) 的情况和 \(a \le b\) 的情况都二分一遍即可。
注意:别忘了还有空子串的情况。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
char a[N];
int sum[N];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",a+1);
int n = strlen(a+1);
for(int i = 1;i<=n;i++)
{
sum[i] = sum[i-1]+a[i]-'0';
}
int minn = 1e9;
for(int i = 1;i<=n;i++)
{
int l = i,r = n,ans = 0;
while(l<=r)
{
int mid = l+r>>1;
if(mid-i+1-(sum[mid]-sum[i-1])<=sum[i-1]+sum[n]-sum[mid])
{
ans = mid;
l = mid+1;
}
else
{
r = mid-1;
}
}
l = i,r = n;
int ans1 = 0;
while(l<=r)
{
int mid = l+r>>1;
if(mid-i+1-(sum[mid]-sum[i-1])>=sum[i-1]+sum[n]-sum[mid])
{
ans1 = mid;
r = mid-1;
}
else
{
l = mid+1;
}
}
if(ans+ans1!=0)
{
if(ans == 0)
{
minn = min(minn,ans1-i+1-(sum[ans1]-sum[i-1]));
}
else if(ans1 == 0)
{
minn = min(minn,sum[i-1]+sum[n]-sum[ans]);
}
else
{
minn = min(minn,min(sum[i-1]+sum[n]-sum[ans],ans1-i+1-(sum[ans1]-sum[i-1])));
}
}
}
printf("%d\n",min(minn,sum[n]));
}
return 0;
}

浙公网安备 33010602011771号