J-Subarray_2019牛客暑期多校训练营(第二场)


题意

有一个只由1,-1组成的数组,给出所有连续的1所在位置,求满足1的个数大于-1的个数的子区间的数量

题解

参考博客:https://www.cnblogs.com/Yinku/p/11221494.html
考虑做前缀和,问题就转化成sum[i]-sum[j] > 0的对数, 由于数据范围较大不可能对整个数组前缀和,可以只计算对答案有贡献的区间的前缀和,需要对给出的线段1进行合并,例如(111) -1 (11)就可以合并,(11) -1-1-1-1-1 (11)就没必要合并,这样可以去掉很多无用的-1区间,最后数组拆分成若干段,对每段分别计算区间数就行
由于sum[i]每次只会+1或-1, 可以利用lazy数组O(1)的求出大于sum[i]的前缀和的个数,具体实现看代码

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
typedef long long ll;
const int mx = 1e7+5;
const int INF = 0x3f3f3f3f;
 
int l[mx], r[mx], f[mx], g[mx];
int sum[mx*3], b[mx*3], lazy[mx*3];
 
int main() {
    int n;
    scanf("%d", &n);
 
    for (int i = 1; i <= n; i++) scanf("%d%d", &l[i], &r[i]);
    f[1] = r[1] - l[1] + 1;
    for (int i = 2; i <= n; i++)
        f[i] = max(0, f[i-1] - (l[i] - r[i-1] - 1)) + r[i] - l[i] + 1;
    g[n] = r[n] - l[n] + 1;
    for (int i = n-1; i >= 1; i--)
        g[i] = max(0, g[i+1] - (l[i+1] - r[i] - 1)) + r[i] - l[i] + 1;
 
    int base = 1e7, i = 1;
    ll ans = 0;
    while (i <= n) {
        int j = i+1;
        while (j <= n && f[j-1] + g[j] >= l[j] - r[j-1] - 1) j++;
        j--;
 
        int left = max(0, l[i] - g[i]), right = min((int)1e9, r[j]+f[j])-1;
 
        int t = i, mi = INF, ma = 0;
        sum[0] = 0;
        for (int k = left; k <= right; k++) {
            if (k >= l[t] && k <= r[t])
                sum[k - left + 1] = sum[k - left] + 1;
            else
                sum[k - left + 1] = sum[k - left] - 1;
            if (k == r[t]) t++;
            mi = min(mi, sum[k - left + 1] + base);
            ma = max(ma, sum[k - left + 1] + base);
            b[sum[k - left + 1] + base]++;
        }
 
        for (int k = ma-1; k >= mi; k--) b[k] += b[k+1];
 
        ans += b[base+1];
        for (int k = left; k <= right; k++) {
            t = sum[k - left + 1] + base;
            b[t+1] -= lazy[t+1];
            lazy[t] += lazy[t+1] + 1;
            lazy[t+1] = 0;
            ans += b[t+1];
        }
        for (int k = mi; k <= mx; k++) b[k] = 0, lazy[k] = 0;
 
        i = j+1;
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2019-07-26 18:48  奔跑的蜗new  阅读(207)  评论(1编辑  收藏  举报