zoukankan      html  css  js  c++  java
  • 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
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    2015年中国500强企业
    汇编语言
    oracle数据库学习路线
    OI生涯回忆录
    NOIP 2020游记
    CF223B Two Strings 题解
    CSP-S 2020游记
    CSP/NOIP 注意事项(2020)
    Luogu P6583 回首过去 题解
    Luogu P2210 Haywire 题解
  • 原文地址:https://www.cnblogs.com/bpdwn-cnblogs/p/11252185.html
Copyright © 2011-2022 走看看