zoukankan      html  css  js  c++  java
  • Codeforces 193D Two Segments 解题报告

    先是在蓝桥杯的网站上看到一道题:

      给出1~n的一个排列,求出区间内所有数是连续自然数的区间的个数。n<=50000。

     

    由于数据较弱,即使用O(N^2)的算法也能拿到满分。

     

    于是在CF上发现了这一题:

      给一个1~n的排列,在这个排列中选出两段区间,求使选出的元素排序后构成公差为1的等差数列的方案数。选出的两段区间中元素构成的集合相同时视为同一种方案。N<=300000。

     

    可以发现CF的这题是上一题的强化版。

     

    虽然蓝桥的题目是简化版,但正确的做法似乎没有CF这道题容易想到。

    蓝桥的题首先想到的应该是枚举区间,判断区间最大值与最小值的差是否等于区间长度,这样时间复杂度是O(N^2)。

    似乎不知道该如何优化。

    因此先从CF这题考虑一个比较容易想到的做法:

      考虑自然序列[l,r],假设已经知道[l,r]在读入序列中被分成了几段(假设f[l,r]=k),那么在加入数l-1时,只需判断l-1这个数是单独构成一段(f[l-1,r]=k+1),还是并入了其中一段(f[l-1,r]=k),或者连接了两段(f[l,r]=k-1).当k等于1或2时就可以累加答案.使用这样的方法同样只需要枚举区间左右端点就可以得到一个O(n^2)的算法.再进一步考虑自然序列[l,i],(l<=i<=n).假设t=l-1,在读入序列的左右分别是数x和y(假设x<y).那么对应的f[l-1,m] 中m对应的区间要进行相应的更新(+1,-1),这里可以用线段树来维护.和查询.如此只需要n到1循环l,可以在O(nlogn)的时间复杂度实现.

      对于蓝桥杯的题,假设当前枚举的数是i令[l,r],为f[i,l~r]数值区间.线段树需要维护的东西有区间内最小值,区间内等于最小值的数的个数.CF题需要额外维护,等于区间最小值+1的数的个数.

     

    蓝桥杯的代码:

    #include <iostream>
    #include <cstdio>
    using namespace std;
    #define lson x<<1
    #define rson x<< 1 | 1
    const int MAXN = 50009;
    struct Segtree {
        int l, r, val, add, tol;
    } St[MAXN << 2];
    int p[MAXN], a[MAXN];
    int n, ans;
    void Build (int x, int l, int r) {
        St[x].l = l, St[x].r = r;
        St[x].tol = r - l + 1;
        if (l == r) return;
        int mid = (l + r) >> 1;
        Build (lson, l, mid), Build (rson, mid + 1, r);
    }
    void push (int x) {
        if (St[x].add == 0) return;
        St[lson].add +=St[x].add, St[rson].add += St[x].add;
        St[lson].val += St[x].add, St[rson].val += St[x].add;
        St[x].add = 0;
    }
    void update (int x) {
        St[x].val = min (St[lson].val, St[rson].val);
        St[x].tol = St[lson].tol * (St[x].val == St[lson].val) + St[rson].tol * (St[x].val == St[rson].val);
    }
    void Modify (int x, int l, int r, int key) {
        if (St[x].l >= l && St[x].r <= r) {
            St[x].val += key, St[x].add += key;
            return;
        }
        push (x);
        int mid = (St[x].l + St[x].r) >> 1;
        if (mid >= l) Modify (lson, l, r, key);
        if (mid < r) Modify (rson, l, r, key);
        update (x);
    }
    void Query (int x, int l, int r) {
        if (St[x].l >= l && St[x].r <= r) {
            ans += St[x].tol * (St[x].val == 1);
            return;
        }
        push (x);
        int mid = (St[x].l + St[x].r) >> 1;
        if (mid >= l) Query (lson, l, r);
        if (mid < r) Query (rson, l, r);
    }
    int main() {
        scanf ("%d", &n);
        for (int i = 1, x; i <= n; i++) {
            scanf ("%d", &x);
            p[x] = i;
        }
        Build (1, 1, n);
        for (int i = n; i; i--) {
            a[p[i]] = i;
            int x = a[p[i] - 1], y = a[p[i] + 1];
            if (x > y) swap (x, y);
            if (x && y) Modify (1, i, x - 1, 1), Modify (1, y, n, -1);
            else if (y) Modify (1, i, y - 1, 1);
            else Modify (1, i, n, 1);
            Query (1, i, n);
        }
        printf ("%d", ans);
    }
    View Code

    CF的代码:

    #include <iostream>
    #include <cstdio>
    using namespace std;
    #define lson x<<1
    #define rson x<< 1 | 1
    const int MAXN = 300009;
    struct Segtree {
        int l, r, val, add, tol, tol2;
    } St[MAXN << 2];
    int p[MAXN], a[MAXN];
    int n;
    long long ans;
    void Build (int x, int l, int r) {
        St[x].l = l, St[x].r = r;
        St[x].tol = r - l + 1;
        if (l == r) return;
        int mid = (l + r) >> 1;
        Build (lson, l, mid), Build (rson, mid + 1, r);
    }
    void push (int x) {
        if (St[x].add == 0) return;
        St[lson].add +=St[x].add, St[rson].add += St[x].add;
        St[lson].val += St[x].add, St[rson].val += St[x].add;
        St[x].add = 0;
    }
    void update (int x) {
        St[x].val = min (St[lson].val, St[rson].val);
        St[x].tol = St[lson].tol * (St[x].val == St[lson].val) + St[rson].tol * (St[x].val == St[rson].val);
        St[x].tol2=St[lson].tol*(St[x].val+1==St[lson].val)+St[rson].tol*(St[x].val+1==St[rson].val);
        St[x].tol2+=St[lson].tol2*(St[x].val==St[lson].val)+St[rson].tol2*(St[x].val==St[rson].val);
    }
    void Modify (int x, int l, int r, int key) {
        if (St[x].l >= l && St[x].r <= r) {
            St[x].val += key, St[x].add += key;
            return;
        }
        push (x);
        int mid = (St[x].l + St[x].r) >> 1;
        if (mid >= l) Modify (lson, l, r, key);
        if (mid < r) Modify (rson, l, r, key);
        update (x);
    }
    void Query (int x, int l, int r) {
        if (St[x].l >= l && St[x].r <= r) {
            ans += St[x].tol * (St[x].val <=2)+St[x].tol2*(St[x].val==1);
            return;
        }
        push (x);
        int mid = (St[x].l + St[x].r) >> 1;
        if (mid >= l) Query (lson, l, r);
        if (mid < r) Query (rson, l, r);
    }
    int main() {
        scanf ("%d", &n);
        for (int i = 1, x; i <= n; i++) {
            scanf ("%d", &x);
            p[x] = i;
        }
        Build (1, 1, n);
        for (int i = n; i; i--) {
            a[p[i]] = i;
            int x = a[p[i] - 1], y = a[p[i] + 1];
            if (x > y) swap (x, y);
            if (x) Modify (1, i, x - 1, 1), Modify (1, y, n, -1);
            else if (y) Modify (1, i, y - 1, 1);
            else Modify (1, i, n, 1);
            Query (1, i, n);
        }
        printf ("%I64d", ans-n);
    }
    View Code
  • 相关阅读:
    HDU 1261 字串数(排列组合)
    Codeforces 488C Fight the Monster
    HDU 1237 简单计算器
    POJ 2240 Arbitrage
    POJ 3660 Cow Contest
    POJ 1052 MPI Maelstrom
    POJ 3259 Wormholes
    POJ 3268 Silver Cow Party
    Codesforces 485D Maximum Value
    POJ 2253 Frogger(最短路)
  • 原文地址:https://www.cnblogs.com/keam37/p/4335914.html
Copyright © 2011-2022 走看看