zoukankan      html  css  js  c++  java
  • POJ 数据结构(2)

    数据结构

    线段树

    poj2528,poj2828,poj2777,poj2886,poj2750, poj2892(区间更新)

    静态二叉检索树,平衡树treap,splay

    poj2482, poj3468

    树状树组

    poj1195,poj3321, poj2352

    RMQ

    poj3264,poj3368

    并查集的高级应用

    poj1703, poj2492

    KMP算法

    poj1961,poj2406

     

      线段树

    poj 2528

    以前做过多次,思路很明显,线段数分区间染色,最后统计颜色的个数。我犯了一个极度二百五的错误,导致这题提交5次!!!col的初始化是-1,更新时我当成0了。T_T

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for(i = 0; i < n; ++i)
    #define FOR(i, l, h)    for(i = l; i <= h; ++i)
    #define FORD(i, h, l)   for(i = h; i >= l; --i)
    #define L(x)    x << 1
    #define R(x)    x << 1 | 1
    #define MID(l, r)  (l + r) >> 1
    
    using namespace std;
    
    const int N = 20010;
    
    struct node {
        int l, r;
        int col;
    } seg[N<<2];
    
    int num1[N];
    int num[N];
    int n;
    int x[N], y[N];
    bool cnt[N<<2];
    
    int find(int x) {
        int l, r, mid;
        l = 0; r = n;
        while(l <= r) {
            mid = (l + r) >> 1;
            if(num[mid] == x)   return mid;
            else if(num[mid] > x)   r = mid - 1;
            else    l = mid + 1;
        }
        return -1;
    }
    
    void creat(int t, int l, int r) {
        seg[t].l = l;
        seg[t].r = r;
        seg[t].col = -1;
        if(r == l)  return ;
        int mid = MID(l, r);
        creat(L(t), l, mid);
        creat(R(t), mid + 1, r);
    }
    
    void updata(int t, int l, int r, int col) {
        if(seg[t].l >= l && seg[t].r <= r) {
    
            seg[t].col = col; return ;
        }
        if(seg[t].col == col)   return ;
        if(seg[t].col >= 0) {
            seg[L(t)].col = seg[t].col;
            seg[R(t)].col = seg[t].col;
            seg[t].col = -1;
        }
        int mid = MID(seg[t].l, seg[t].r);
        if(l > mid)    updata(R(t), l, r, col);
        else if(r <= mid)   updata(L(t), l, r, col);
        else {
            updata(L(t), l, mid, col);
            updata(R(t), mid + 1, r, col);
        }
    }
    
    void query(int t, int l, int r) {
    
        if(seg[t].col >= 0) {//printf("%d %d %d\n", l, r, seg[t].col);
            cnt[seg[t].col] = true; return ;
        }
    
        if(seg[t].l == seg[t].r)    return ;
    
        int mid = MID(seg[t].l, seg[t].r);
        query(L(t), l, mid);
        query(R(t), mid + 1, r);
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int t, i, j, m;
        int f, u, v, ans;
        scanf("%d", &t);
        while(t--) {
            scanf("%d", &m);
            CL(num, 0);
            REP(i, m)   {
                scanf("%d%d", &x[i], &y[i]);
                num1[i*2] = x[i]; num1[i*2+1] = y[i];
            }
            n = m*2; i = j = 0;
            sort(num1, num1 + n);
    
            while(i < n) {
                f = num1[i];
                while(num1[i] == f && i < n) i++;
                num[j++] = f;
            }
            n = j;
            creat(1, 0, n);
    
            REP(i, m) {
                u = find(x[i]);
                v = find(y[i]);
                //printf("%d %d %d\n", u, v, i);
                updata(1, u, v, i);
            }
            CL(cnt, 0);  ans = 0;
            query(1, 0, n);
    
            REP(i, m)   if(cnt[i])  ans++;
            printf("%d\n", ans);
        }
        return 0;
    }

    poj 2828


    题意:插队,第i个人插在pos[i]位置的后边,输出最后位置的val[i]顺序。

    思路:以前做过,又忘了。。。http://www.cnblogs.com/vongang/archive/2011/08/12/2135816.html

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for(i = 0; i < n; ++i)
    #define FOR(i, l, h)    for(i = l; i <= h; ++i)
    #define FORD(i, h, l)   for(i = h; i >= l; --i)
    #define L(x)    x << 1
    #define R(x)    x << 1 | 1
    #define MID(l, r)  (l + r) >> 1
    
    using namespace std;
    
    const int N = 200010;
    
    struct node {
        int l, r;
        int num;
    } seg[N<<2];
    
    int ans[N];
    int pos[N];
    int num[N];
    bool vis[N];
    int p;
    
    void creat(int t, int l, int r) {
        seg[t].l = l;
        seg[t].r = r;
        seg[t].num = r - l + 1;
        if(l == r)  return ;
        int mid = MID(l, r);
        creat(L(t), l, mid);
        creat(R(t), mid + 1, r);
    }
    
    void updata(int t, int x) {
        if(p != -1) return ;
        --seg[t].num;
        if(seg[t].num == 0 && !vis[seg[t].l])     {p = seg[t].l; vis[p] = true; return ;}
        if(x <= seg[L(t)].num)   updata(L(t), x);
        else    updata(R(t), x - seg[L(t)].num);
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int n, i;
        while(~scanf("%d", &n)) {
            REP(i, n)   scanf("%d%d", &pos[i], &num[i]);
            creat(1, 1, n);
            CL(vis, false);
            FORD(i, n - 1, 0) {
                p = -1;
                updata(1, pos[i] + 1);
                ans[p] = num[i];
            }
            printf("%d", ans[1]);
            FOR(i, 2, n)    printf(" %d", ans[i]);
            cout << endl;
        }
        return 0;
    }

    poj 2777

    很简单的线段树染色问题,开始着色为1,然后根据输入染色,根据query统计线段上有多少种颜色,幸好颜色最多30种,随便搞一下就好了。。。把统计颜色的数组开的太大了,TLE一次。。。

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for(i = 0; i < n; ++i)
    #define FOR(i, l, h)    for(i = l; i <= h; ++i)
    #define FORD(i, h, l)   for(i = h; i >= l; --i)
    #define L(x)    x << 1
    #define R(x)    x << 1 | 1
    #define MID(l, r)  (l + r) >> 1
    
    using namespace std;
    
    const int N = 100010;
    
    struct node {
        int l, r;
        int col;
    } seg[N<<2];
    
    bool col[33], ans;
    
    void creat(int t, int l, int r) {
        seg[t].l = l;
        seg[t].r = r;
        seg[t].col = 1;
        if(l == r)  return ;
        int mid = MID(l, r);
        creat(L(t), l, mid);
        creat(R(t), mid + 1, r);
    }
    
    void updata(int t, int l, int r, int col) {
        if(seg[t].l >= l && seg[t].r <= r) {
            seg[t].col = col;
            return ;
        }
        if(seg[t].col == col)   return ;
        if(seg[t].col >= 0) {
            seg[L(t)].col = seg[t].col;
            seg[R(t)].col = seg[t].col;
            seg[t].col = -1;
        }
        int mid = MID(seg[t].l, seg[t].r);
    
        if(l > mid)     updata(R(t), l, r, col);
        else if(r <= mid)   updata(L(t), l, r, col);
        else {
            updata(L(t), l, mid, col);
            updata(R(t), mid + 1, r, col);
        }
    }
    
    void query(int t, int l, int r) {
        if(seg[t].col >= 0) {
            col[seg[t].col] = true; return ;
        }
        if(seg[t].l == seg[t].r)    return ;
    
        int mid = MID(seg[t].l, seg[t].r);
        if(l > mid)     query(R(t), l, r);
        else if(r <= mid)   query(L(t), l, r);
        else {
            query(L(t), l, mid);
            query(R(t), mid + 1, r);
        }
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int l, t, o, i;
        int a, b, c, ans;
        char s[10];
        scanf("%d%d%d", &l, &t, &o);
        creat(1, 1, l);
        while(o--) {
            scanf("%s%d%d", s, &a, &b);
            if(s[0] == 'C') {
                scanf("%d", &c);
                updata(1, a, b, c);
            } else {
                CL(col, 0); ans = 0;
                query(1, a, b);
                FOR(i, 1, t)    if(col[i])  ans++;
                printf("%d\n", ans);
            }
        }
        return 0;
    }

    poj 2750

    题意读的很费解,说的是一个N个元素拼成一个环,每个元素有一个value值,value在整数范围内。有k次更新,每更新一个求一次环中连续子串的和最大值。

    思路:没思路,开始连题意都没怎么看懂。后来看到很多人转载出题人的解题报告。有人称之为线段树+dp,扯淡!线段树有最优子结构吗?!线段树有后效性吗?!线段树有重叠子问题吗?!线段树归根结底说就是dp的思想!不吐槽了,想起以前看到的一句话,"真的很想直接抄抄他们的代码,假装理解,就这样过去了。"

    把原来的环看成 1...N的数组,这样环中的最大值可能有两种情况取到:Case 1、在1...N之间的某个区间;Case 2、[b, N] + [1, a];

    第一种情况很容易考虑,计一个xmax值,表示根节点所对应的区间上连续子串的和最大值,线段树在线更新就可以。第二种情况可以这样考虑:记一个sum值,表示根节点所对应的区间所有元素的总和,记xmin表示这段区间上最小连续子串。sum - xmin就是第二种情况的值,因为 [a, b]肯定是最小连续子串,才能保证[b, N] , [1, a]是最大连续子串。

    关于xmax, xmin这两个值,可以跟线段树区间更新一样,加lmax, lmin, rmax, rmin。分别表示在一个区间上从左边第一个开始数的最大连续子串,最小连续子串;从右边第一个开始数的最大连续子串,最小连续子串。跟新过程画个区间推一下就可以。

    seg[t].sum = seg[L(t)].sum + seg[R(t)].sum;
    seg[t].xmax = max(max(seg[L(t)].xmax, seg[R(t)].xmax), seg[L(t)].rmax + seg[R(t)].lmax);
    seg[t].xmin = min(min(seg[L(t)].xmin, seg[R(t)].xmin), seg[L(t)].rmin + seg[R(t)].lmin);
    seg[t].lmax = max(seg[L(t)].lmax, seg[L(t)].sum + seg[R(t)].lmax);
    seg[t].rmax = max(seg[R(t)].rmax, seg[R(t)].sum + seg[L(t)].rmax);
    seg[t].lmin = min(seg[L(t)].lmin, seg[L(t)].sum + seg[R(t)].lmin);
    seg[t].rmin = min(seg[R(t)].rmin, seg[R(t)].sum + seg[L(t)].rmin);

    ps:题目有一个限制,不能把所有的元素都包括。所以如果出现seg[1].xmax == seg[1].sum时(全是正数),直接输出seg[1].sum - seg[1].xmin,就可以,其他情况则取max(Case 1, Case2);

     poj 2892

    题意很意思,地道战,Eight Route Army。。。这翻译太有喜感了,哈!

    思路:更新到点,query时找的是区间。比Hotel那道题简单,写着写着发现根本就不需要sum这个值,以需要开一个flag标记这一段是否有被搞掉的点,一个lmax表示这段区间从左边开始数最大连续有多少个点,rmax表示这段区间从右边开始数最大连续有多少个点。。。然后,乱搞一下就ok了。表示很无语的一道题,妹的昨晚写完代码有点晚了,以为有问题就保存一下关电脑走人了,今天来了加上数据一看没问题。。。提交一下1Y了。。。悲剧的人生啊!

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <stack>
    #include <algorithm>
    
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for(i = 0; i < n; ++i)
    #define FOR(i, l, h)    for(i = l; i <= h; ++i)
    #define FORD(i, h, l)   for(i = h; i >= l; --i)
    #define L(x)    x << 1
    #define R(x)    x << 1 | 1
    #define MID(l, r)  (l + r) >> 1
    typedef long long LL;
    
    using namespace std;
    
    const int N = 50010;
    
    struct node {
        int l, r;
        bool flag;
        int lmax, rmax;
        int mid() {
            return (l + r) >> 1;
        }
    } seg[N<<2];
    
    void creat(int t, int l, int r) {
        seg[t].l = l;
        seg[t].r = r;
        seg[t].flag = 0;
        seg[t].lmax = seg[t].rmax = r - l + 1;
        if(l == r)  return ;
        int mid = MID(l, r);
        creat(L(t), l, mid);
        creat(R(t), mid + 1, r);
    }
    
    void updata(int t, int d) {
        if(seg[t].l == seg[t].r) {    //更新到点
            seg[t].flag ^= 1;
            seg[t].lmax ^= 1;
            seg[t].rmax ^= 1;
            return ;
        }
        int mid = MID(seg[t].l, seg[t].r);
        if(d <= mid)    updata(L(t), d);
        else    updata(R(t), d);
        seg[t].lmax = seg[L(t)].lmax + (seg[L(t)].flag == 0 ? seg[R(t)].lmax : 0);
        seg[t].rmax = seg[R(t)].rmax + (seg[R(t)].flag == 0 ? seg[L(t)].rmax : 0);
        seg[t].flag = (seg[L(t)].flag == 0 && seg[R(t)].flag == 0) ? 0 : 1;
    }
    
    int query(int t, int d) {
        if(seg[t].l == seg[t].r) {
            return seg[t].flag == 0;
        }
        int mid = seg[t].mid();
        if(d <= mid) {
            if(d >= mid - seg[L(t)].rmax + 1)    return seg[L(t)].rmax + seg[R(t)].lmax;    
            else    return query(L(t), d);
        } else {
            if(d <= mid + seg[R(t)].lmax)    return seg[R(t)].lmax + seg[L(t)].rmax;
            else    return query(R(t), d);
        }
    }
    
    int st[N];
    int vis[N];
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int n, t, x, top = 0;
        char s[10];
        scanf("%d%d", &n, &t);
        creat(1, 1, n);
        CL(st, 0);
        CL(vis, 0);
        while(t--) {
            scanf("%s", s);
            if(s[0] == 'D') {
                scanf("%d", &x);
                st[top++] = x;
                if(vis[x] == 0)     updata(1, x);
                vis[x]++;
            } else if(s[0] == 'Q') {
                scanf("%d", &x);
                printf("%d\n", query(1, x));
            } else {
                x = st[--top];
                vis[x]--;
                if(!vis[x]) updata(1, x);
            }
        }
        return 0;
    }

     POJ 2886 详见:http://www.cnblogs.com/vongang/archive/2012/10/28/2743149.html

    树状数组


    poj 1195

    二维树状数组,注意的几点,instruction = 2时输入l b r t,  l <= x <= r,  b <= y <= t

    ans = sum(r + 1, t + 1) + sum(l, b) - sum(r + 1, b) - sum(l, t + 1);

    poj 3321

    题意:苹果常在叉上,某些叉有边相连,Q i表示求以i根的树上有多少苹果。C i表示i节点如果有苹果则摘掉,如果没有则加一个。

    思路:dfs整棵树,low[i]表示进入以i为根的子树的开始时间,high[i]表示从i出来是的时间,这样[low[i], high[i]]就能包括以i为根的子树。

    用low[], high[]编好号以后就可以用树状数组维护了。。。很经典的问题。Mark!

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for(i = 0; i < n; ++i)
    #define FOR(i, l, h)    for(i = l; i <= h; ++i)
    #define FORD(i, h, l)   for(i = h; i >= l; --i)
    #define L(x)    x << 1
    #define R(x)    x << 1 | 1
    #define MID(l, r)  (l + r) >> 1
    typedef long long LL;
    
    using namespace std;
    
    const int N = 100010;
    
    struct node {
        int to;
        int next;
    } g[N << 2];
    
    int head[N], t, ind;
    int low[N], high[N];
    int c[N], n;
    bool vis[N];
    
    void init() {
        CL(head, -1);
        CL(vis, false);
        CL(c, 0);
        t = ind = 0;
    }
    
    void add(int u, int v) {
        g[t].to = v; g[t].next = head[u]; head[u] = t++;
    }
    
    void dfs(int t) {
        vis[t] = true;
        low[t] = ++ind;
        int i;
        for(i = head[t]; i != -1; i = g[i].next)
            if(!vis[g[i].to])   dfs(g[i].to);
        high[t] = ind;
    }
    
    int lowbit(int i) {
        return i&(-i);
    }
    
    void Modify(int x, int d) {
        while(x <= n) {
            c[x] += d;
            x += lowbit(x);
        }
    }
    
    int sum(int x) {
        int res = 0;
        while(x > 0) {
            res += c[x];
            x -= lowbit(x);
        }
        return res;
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int u, v, i;
        char s[10];
        init();
        scanf("%d", &n);
        REP(i, n - 1) {
            scanf("%d%d", &u, &v);
            add(u, v);
        }
        dfs(1);
        FOR(i, 1, n)    Modify(low[i], 1);
    
        scanf("%d", &u);
        while(u--) {
            scanf("%s%d", s, &v);
            if(s[0] == 'Q') {
                printf("%d\n", sum(high[v]) - sum(low[v] - 1));
            } else {
                if(vis[v]) {
                    Modify(low[v], -1);
                    vis[v] = false;
                } else {
                    Modify(low[v], 1);
                    vis[v] = true;
                }
            }
        }
        return 0;
    } 

    poj 2352

    经典树状数组应用,做过好几遍了。。。有一个地方想错了,wa了3次。。。

    void Modify(int x, int k) {
        while(x < M) {    //这里不能写成x < n,因为是按x的值插入的。M = 32010;
            c[x] += k;
            x += lowbit(x);
        }
    }

     RMQ

    poj 3264

    以前用线段树写的,今天用RMQ写了一下,发现RMQ还是很猛的,时间是线段树的一半(也可能是我的线段树些搓了。。。)

    详见:http://www.cnblogs.com/vongang/archive/2012/05/03/2481196.html

    poj 3368

    题意很简单,求给一个序列,Q次询问,每次求 [a, b]区间里的重复数字最多重多少次。

    思路:对一个序列tnum[]

    -1 -1 1 1 1 1 3 10 10 10 把它处理成num[]

     1  2 1 2 3 4 1  1   2   3

    按num[]进行RMQ,然后[a, b]区间的两端做单独统计 x表示跟tnum[a]相同的有多少个,y表示跟tnum[b]相同的有多少个(都是相邻的)。中间的用rmq求。(可以标记每一个tnum[i]的起始位置be[i], ed[i])

    ps:开始拍搓了,没考虑a == b的情况,wa掉两次后一测数据。。。偶滴神啊!这么明显的错误。。。

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <stack>
    #include <cmath>
    #include <algorithm>
    
    
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for(i = 0; i < n; ++i)
    #define FOR(i, l, h)    for(i = l; i <= h; ++i)
    #define FORD(i, h, l)   for(i = h; i >= l; --i)
    #define L(x)    x << 1
    #define R(x)    x << 1 | 1
    #define MID(l, r)  (l + r) >> 1
    typedef long long LL;
    
    using namespace std;
    
    const int N = 200010;
    const int M = 100010;
    const int fix = 100000;   //修正值,因为有负数
    
    int Max[M][20];
    int num[M], tnum[M];
    int be[N], ed[N];
    int n;
    
    void init() {
        int i, j, m;
        FOR(i, 1, n) {
            Max[i][0] = num[i];
            //Min[i][0] = num[i];
        }
        m = int(log(double(n))/log(2.0));
        FOR(j, 1, m){
            FOR(i, 1, n - (1<<j) + 1) {
                Max[i][j] = max(Max[i][j-1], Max[i+ (1<<(j-1))][j-1]);
                //Min[i][j] = min(Min[i][j-1], Min[i+ (1<<(j-1))][j-1]);
            }
        }
    }
    
    int solve(int s, int e) {
        int k = log(double(e - s + 1))/log(2.0);
        int x = max(Max[s][k], Max[e - (1<<k) + 1][k]);
        //int y = min(Min[s][k], Min[e - (1<<k) + 1][k]);
        return x;
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int q, i, a, b, t, x, y;
        while(scanf("%d", &n), n) {
            scanf("%d", &q);
            CL(num, 0); CL(tnum, 0);
            CL(Max, 0); CL(be, 0);
            CL(ed, 0);
            FOR(i, 1, n)    scanf("%d", tnum + i);
            FOR(i, 1, n) {
                t = tnum[i]; x = 1;
                be[tnum[i]+fix] = i;   //tnum[i]的开始位置
                while(tnum[i] == t) {
                    num[i++] = x++;
                }
                --i;
                ed[tnum[i]+fix] = i;   //结束位置
            }
            init();
            while(q--) {
                scanf("%d%d", &a, &b);
                if(a == b)  {printf("1\n"); continue;}    //pay attention to this !
                
                x = min(ed[tnum[a] + fix], b) - a + 1;
                y = b - max(a, be[tnum[b] + fix]) + 1;
                a = min(ed[tnum[a] + fix], b) + 1;
                b = max(be[tnum[b] + fix], a) - 1;
    
                if(b <= a)  printf("%d\n", max(x, y));
                else    printf("%d\n", max(max(x, y), solve(a, b)));
            }
        }
        return 0;
    }

    并查集的高级应用

    poj 1703

    题意:一个城市有两个流氓团,D a b表示a b不再同一个同一个流氓团里边。A a b表示询问a b是否在一个流氓团里边。根据情况输出 "In the same gang.", "In different gangs." and "Not sure yet."

    思路:好吧是我想复杂了。。。昨晚有点困,然后一直在纠结。。。其实相当于二分图2-SAT问题。a b矛盾则连接 a -> b'  a' -> b。query时判断a ,b是否在同一集合里,是则输出“In the same”,如果a , b'在同一集合里则表示ab不再同一集合里。否则就不能确定a, b的关系。

    poj 2492

    跟上题差不多,水题。这题是分两个性别,输入a b表示a和b可以交配,问是否出现同性恋。

    合并 a -> b'    a' -> b;判断 find(a) == find(b)表示出现同性恋。。。

    KMP算法

    poj 1916 && poj 2406 解法:http://www.cnblogs.com/vongang/archive/2012/05/04/2483419.html

  • 相关阅读:
    Serialize&Deserialize
    Async&Await
    Generic
    Enum
    Array
    String
    Nullable
    Collection
    Class&Struct
    Interface
  • 原文地址:https://www.cnblogs.com/vongang/p/2476133.html
Copyright © 2011-2022 走看看