上海月赛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;
}
posted @ 2025-05-26 19:57  林晋堃  阅读(93)  评论(0)    收藏  举报