zoukankan      html  css  js  c++  java
  • SPOJ GSS系列解题报告

    众所周知,(GSS)是用来练习线段树的一个非常好的系列。

    依稀记得(8)月初还在(qbxt)时,某钟姓神仙说过 :“大家可以把(GSS)系列写一下,大概够大家写一个月了。”虽然我很不想承认,但他确实说中了(……)虽然中间夹有做各种杂题和一些模拟赛(……)

    题解

    GSS 1

    (GSS)系列最简单的一道题,也算是后面题目的一个基础。

    给定一个序列,维护区间最大子段和。

    我们维护这样几个东西(:lsum, rsum, maxsum, maxval.)

    分别表示区间最大前缀和,区间最大后缀和,区间最大子段和,区间最大值,其中最大前缀和与最大后缀和用于辅助求出最大子段和。

    我们考虑一下如何合并左右两个区间:

    • 对于当前区间的(lsum),我们取左区间的(lsum)和左区间的(sum+)右区间的(lsum)中的最大值来更新。
    • 对于当前区间的(rsum),我们采取与维护(lsum)相似的方式维护,取右区间的(rsum)和左区间的(lsum+)右区间的(sum)中的最大值来更新。
    • 对于当前区间的(maxsum),即最大子段和,我们取左区间的(maxsum)和右区间的(maxsum)和左区间的(rsum+)右区间的(lsum)中的最大值来更新当前区间的(maxsum)
    • 对于当前区间的(maxval),正常维护即可。之所以要维护(maxval),是因为当我们发现要询问的区间的最大值小于0时,直接输出当前区间的(maxval)即可。

    另外,关于最大子段和,版本不同对于最大子段和是否可以为空要求不同,所以代码细节可能会比较多,对于像我一样粗心的人可能会不太友好(……)

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 5e4 + 5;
    int w, ch, n, m, a[maxn];
    
    template<class T>
    T read(T &x)
    {
        x = 0, w = 1, ch = getchar();
        while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
        while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
        return x *= w;
    }
    
    struct node {
        int l, r, maxsum, sum, maxval;
        int fmax, bmax;
    }z[maxn << 2];
    
    node operator + (const node &a, const node &b)
    {
        node ans;
        ans.l = a.l;
        ans.r = b.r;
        ans.sum = a.sum + b.sum;
        ans.maxval = max(a.maxval, b.maxval);
        ans.maxsum = max(a.maxsum, max(b.maxsum, a.bmax + b.fmax));
        ans.fmax = max(a.fmax, a.sum + b.fmax);
        ans.bmax = max(b.bmax, b.sum + a.bmax);
        return ans;
    }
    
    void pushup(int rt)
    {
        z[rt] = z[rt << 1] + z[rt << 1 | 1];
    }
    
    void build(int rt, int l, int r)
    {
        if (l == r) {
            z[rt].l = l, z[rt].r = r;
            z[rt].sum = z[rt].maxval = a[l];
            if (z[rt].sum < 0) {
                z[rt].maxsum = z[rt].fmax = z[rt].bmax = 0;
            }
            else {
                z[rt].maxsum = z[rt].fmax = z[rt].bmax = a[l];
            }
            return ;
        }
        int mid = (l + r) >> 1;
        build (rt << 1, l, mid);
        build (rt << 1 | 1, mid + 1, r);
        pushup(rt);
    }
    
    node query(int rt, int l, int r)
    {
        if (z[rt].l == l && z[rt].r == r)
            return z[rt];
        int mid = (z[rt].l + z[rt].r) >> 1;
        if (r <= mid)
            return query(rt << 1, l, r);
        if (l > mid)
            return query(rt << 1 | 1, l, r);
        return query(rt << 1, l, mid) + query(rt << 1 | 1, mid + 1, r);
    }
    
    int main()
    {
        read(n);
        for (int i = 1; i <= n; i++) read(a[i]);
        build(1, 1, n);
        read(m);
        int L, R;
        while (m--) {
            read(L), read(R);
            node ans = query(1, L, R);
            if (ans.maxval < 0) cout << ans.maxval << '
    ';
            else cout << ans.maxsum << '
    ';
        }
        return 0;
    }
    

    GSS 2

    讲道理,窝(jio)得这是(GSS)系列中最难的一道题,所以窝也不知道为什么这道题会放在第二位。

    看题面,我们会发现这道题仅仅比(GSS 1)多了一个判重的操作,然后(……)他的难度就上天了!!

    遇到这种需要去重的问题,我们采取一个比较套路的方法:离线。

    没错!!这是一道离线神题!!

    考虑到我们需要去重,(GSS1)中用到的记录区间前缀,后缀的方法就不再适用了,所以我们需要一棵很强的线段树来维护一些很强的东西!!

    先说思路和做法,对于本题我们维护的线段树,线段树的叶子结点(j)表示(j)到当前节点(i)这个区间的最大子段和,即(:)

    (max{A[l]+A[l+1]+……+A[r]}) 其中 (j leq l leq r leq i)

    我们维护这样几个东西(:)当前区间最大子段和(maxsum),区间历史最大子段和(hismaxsum),懒标记(tag),历史最大懒标记(histag)

    我们将所有的询问按照右端点排序,每次将一个数插入线段树中,仔细思考一下,我们更新之前每一个节点,其实就是在进行一个区间加的过程,但我们需要去重,一个简便的方法(:)记录一个(pre[a[j]])来记录(a[j])前一次出现的位置,这样我们每次修改({pre[a[j]]+1, i})即可避免(a[j])对之前造成影响,当我们当前节点与询问的右端点相同时,即可更新并记录答案,更新答案时我们发现,当前的(maxsum)不一定是最大子段和,而(hismaxsum)才是,原因是显然的。

    代码细节(:)

    • 在进行(pushdonw)时,对于子区间的(hsimaxsum)(histag)直接下放当前区间的(histag),原因和更新答案的原理是相似的。

    • 在向线段树中插入时,要注意更新的顺序,先更新(maxsum)(tag),再更新(hismaxsum)(histag),这样就保证了区间的连续性。

    • 剩下就是一些细节要注意,例如记录(pre)数组时,为防止出现负下标,把每个数整体向后移100000位(……)

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    
    namespace LXC
    {
        template<class T>
        inline T read(T &x)
        {
            x = 0; int w = 1, ch = getchar();
            while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
            while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
            return x *= w;
        }
    
        template<class T>
        inline T max(const T a, const T b)
        {
            return a > b ? a : b;
        }
    }
    using namespace LXC;
    using std::sort;
    
    const int maxn = 2e5 + 5;
    int n, m, a[maxn], pre[maxn << 1];
    int x[maxn], y[maxn], idx[maxn];
    
    bool cmp(int a, int b)
    {
        return y[a] < y[b];
    }
    
    struct Node {
        int l, r;
        ll maxsum, maxsum_old; // 当前贡献最大值   从开始到现在历史贡献最大值
        ll tag, tag_old; // 懒标记 从上次pushdown到现在最大懒标记
    }z[maxn << 2], ans[maxn];
    
    Node operator + (const Node &a, const Node &b)
    {
        Node ans;
        ans.l = a.l;
        ans.r = b.r;
        ans.maxsum = max(a.maxsum, b.maxsum);
        ans.maxsum_old = max(a.maxsum_old, b.maxsum_old);
        ans.tag = ans.tag_old = 0;
        return ans;
    }
    
    void pushdown(int rt)
    {
        int ls = rt << 1, rs = rt << 1 | 1;
        ll v_old = z[rt].tag_old, v = z[rt].tag;
        z[ls].maxsum_old = max(z[ls].maxsum_old, z[ls].maxsum + v_old);
        z[ls].tag_old = max(z[ls].tag_old, z[ls].tag + v_old);
        z[ls].maxsum += v;
        z[ls].tag += v;
        z[rs].maxsum_old = max(z[rs].maxsum_old, z[rs].maxsum + v_old);
        z[rs].tag_old = max(z[rs].tag_old, z[rs].tag + v_old);
        z[rs].maxsum += v;
        z[rs].tag += v;
        z[rt].tag = z[rt].tag_old = 0;
    }
    
    void modify(int rt, int L, int R, int l, int r, ll w)
    {
        if (L <= l && r <= R) {
            z[rt].maxsum += w, z[rt].maxsum_old = max(z[rt].maxsum_old, z[rt].maxsum);
            z[rt].tag += w, z[rt].tag_old = max(z[rt].tag_old, z[rt].tag);
            return ;
        }
        int mid = (l + r) >> 1;
        pushdown(rt);
        if (L <= mid)
            modify(rt << 1, L, R, l, mid, w);
        if (R > mid)
            modify(rt << 1 | 1, L, R, mid + 1, r, w);
        z[rt] = z[rt << 1] + z[rt << 1 | 1];
    }
    
    Node query(int rt, int L, int R, int l, int r)
    {
        if (L <= l && r <= R) return z[rt];
        int mid = (l + r) >> 1;
        pushdown(rt);
        if (R <= mid)
            return query(rt << 1, L, R, l, mid);
        if (L > mid)
            return query(rt << 1 | 1, L, R, mid + 1, r);
        return query(rt << 1, L, R, l, mid) + query(rt << 1 | 1, L, R, mid + 1, r);
    }
    
    int main()
    {
        read(n);
        for (int i = 1; i <= n; i++) read(a[i]);
        read(m);
        for (int i = 1; i <= m; i++) {
            read(x[i]), read(y[i]);
            idx[i] = i;
        }
        sort(idx + 1, idx + m + 1, cmp);
        int j = 1;
        for (int i = 1; i <= n; i++) {
            modify(1, pre[a[i] + 100000] + 1, i, 1, n, a[i]);
            pre[a[i] + 100000] = i;
            while (y[idx[j]] == i && j <= m) ans[idx[j]] = query(1, x[idx[j]], y[idx[j]], 1, n), j++;
        }
        for (int i = 1; i <= m; i++) printf("%lld
    ", ans[i].maxsum_old);
        return 0;
    }
    
    

    GSS 3

    没什么可说的,依旧是查询最大子段和,只比(GSS1)多了一个单点修改。

    当然,单点修改对于我们的维护是没有任何影响的,但如果是区间修改(……)标记的下放可能会比较恶心,有时间可以考虑一下。

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int maxn = 5e4 + 5;
    int w, ch, n, m, a[maxn];
    
    template<class T>
    T read(T &x)
    {
        x = 0, w = 1, ch = getchar();
        while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
        while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
        return x *= w;
    }
    
    struct node {
        int l, r, maxsum, sum, maxval;
        int fmax, bmax;
    }z[maxn << 2];
    
    node operator + (const node &a, const node &b)
    {
        node ans;
        ans.l = a.l;
        ans.r = b.r;
        ans.sum = a.sum + b.sum;
        ans.maxval = max(a.maxval, b.maxval);
        ans.maxsum = max(a.maxsum, max(b.maxsum, a.bmax + b.fmax));
        ans.fmax = max(a.fmax, a.sum + b.fmax);
        ans.bmax = max(b.bmax, b.sum + a.bmax);
        return ans;
    }
    
    void pushup(int rt)
    {
        z[rt] = z[rt << 1] + z[rt << 1 | 1];
    }
    
    void build(int rt, int l, int r)
    {
        if (l == r) {
            z[rt].l = l, z[rt].r = r;
            z[rt].sum = z[rt].maxval = a[l];
            if (z[rt].sum < 0) {
                z[rt].maxsum = z[rt].fmax = z[rt].bmax = 0;
            }
            else {
                z[rt].maxsum = z[rt].fmax = z[rt].bmax = a[l];
            }
            return ;
        }
        int mid = (l + r) >> 1;
        build (rt << 1, l, mid);
        build (rt << 1 | 1, mid + 1, r);
        pushup(rt);
    }
    
    void modify(int rt, int x, int y)
    {
        if (z[rt].l == z[rt].r) {
            if (y < 0){
                z[rt].maxsum = z[rt].fmax = z[rt].bmax = 0;
                z[rt].sum = z[rt].maxval = y;
            }
            else
                z[rt].sum = z[rt].maxval = z[rt].maxsum = z[rt].fmax = z[rt].bmax = y;
            return ;
        }
        int mid = (z[rt].l + z[rt].r) >> 1;
        if (x <= mid)
            modify(rt << 1, x, y);
        else
            modify(rt << 1 | 1, x, y);
        pushup(rt);
    }
    
    node query(int rt, int l, int r)
    {
        if (z[rt].l == l && z[rt].r == r)
            return z[rt];
        int mid = (z[rt].l + z[rt].r) >> 1;
        if (r <= mid)
            return query(rt << 1, l, r);
        if (l > mid)
            return query(rt << 1 | 1, l, r);
        return query(rt << 1, l, mid) + query(rt << 1 | 1, mid + 1, r);
    }
    
    int main()
    {
        read(n);
        for (int i = 1; i <= n; i++) read(a[i]);
        build(1, 1, n);
        read(m);
        int opt, L, R;
        while (m--) {
            read(opt), read(L), read(R);
            if (opt == 0)
                modify(1, L, R);
            else {
                node ans = query(1, L, R);
                if (ans.maxval < 0) cout << ans.maxval << '
    ';
                else cout << ans.maxsum << '
    ';
            }
        }
        return 0;
    }
    
    

    GSS 4

    两个操作(:)区间开方,区间求和。

    区间求和是很常规的操作,我们暂不考虑。

    来看这个比较难搞的区间开方操作,很容易就可以想到对一个区间的(sum)进行开方,但我们仔细一想,开方和和开方是肯定不一样的,所以我们(……)暴力来搞。

    仔细算一下,一个(10^{18})级别的数,只要对其开方六次,他就会变成(1),我们就可以不再对它进行开方,这样就省去了大量的冗余操作,时间复杂度上限(O(6 * 10^5)),这是可以接受的。

    需要一提的是,这道题目是多组测试数据。不过它好像没什么数组需要清空……

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #define LL long long
    
    using namespace std;
    
    const int maxn = 1000005;
    
    LL L[maxn<<2], R[maxn<<2], a[maxn<<2], tag[maxn<<2], sum[maxn<<2], ans;
    int n, m;
    
    void pushup(int rt)
    {
    	sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    	tag[rt] = tag[rt<<1] && tag[rt<<1|1];
    }
    
    void build(int rt,int l,int r)
    {
    	L[rt] = l, R[rt] = r;
    	if (l == r) {
    		sum[rt] = a[l];
    		if (a[l] == 1) tag[rt] = 1;
    		else tag[rt] = 0;
    		return ;
    	}
    	int mid = (l+r) >> 1;
    	build (rt << 1, l, mid);
    	build (rt << 1 | 1, mid + 1, r);
    	pushup(rt);
    }
    
    void modify(int rt, int l, int r)
    {
    	if (tag[rt]) return ;
    	if (L[rt] == R[rt]) {
    		sum[rt] = (LL)(sqrt(sum[rt]));
    		if (sum[rt] <= 1)
                tag[rt] = 1;
    		return ;
    	}
    	int mid = (L[rt] + R[rt]) >> 1;
    	if (r <= mid)
    		modify(rt << 1, l, r);
    	if (l > mid)
    		modify(rt << 1 | 1, l, r);
    	if (l <= mid && r > mid) {
    		modify(rt << 1, l, mid);
    		modify(rt << 1 | 1, mid + 1, r);
    	}
    	pushup(rt);		
    }
    
    LL query(int rt, int l, int r)
    {
    	if (L[rt] == l && R[rt] == r) return sum[rt];
    	int mid = (L[rt] + R[rt]) >> 1;;
    	if (r <= mid)
    		return query(rt << 1, l, r);
    	if (l > mid)
    		return query(rt << 1 | 1, l, r);
    	return query(rt << 1, l, mid) + query(rt << 1 | 1, mid + 1, r);		
    }
    int main()
    {
    	int Case = 0;
    	while (scanf("%d", &n) != EOF) {
            printf("Case #%d:
    ",++Case);
            for (int i = 1; i <= n; i++) cin >> a[i];
            build (1, 1, n);
            scanf("%d", &m);
            int opt, x, y, i = 1;
            while (m--) {
                scanf("%d", &opt);
                if (opt == 0) {
                    scanf("%d%d", &x, &y);
                    if (x > y) swap(x, y); 
                    modify(1, x, y);
                }
                else {
                    scanf("%d%d", &x, &y);
                    if(x > y) swap(x, y);
                    printf("%lld
    ", query(1, x, y));
                }
            }
            puts("");
    	}
    	return 0;
    }
    

    GSS 5

    依旧是求最大子段和的问题,不过它强制左右端点在两个区间({x_1, y_1} {x_2, y_2})中。

    我们考虑分类讨论:

    • (y_1 < x_2)时,即两个区间不重叠,那么最大子段和很显然就是({x_1,y_1})的最大后缀和(+{y_1,x_2})的区间和(+{x_2,y_2})的最大前缀和。

    图片较简陋,凑合着看……

    • (x_2 < y_1)时,即两个区间有重叠部分,如同上图描述的情况,这时,我们需要考虑四种情况,区间({y_1,x_2})的区间和(()上图蓝色线段()),区间({x_1,y_1})的最大后缀和(+{y_1,x_2})的区间和(()上图黄色线段()),区间({y_1,x_2})的区间和(+{x_2,y_2})的最大前缀和(()上图粉色线段()),区间({x_1,y_1})的最大后缀和(+{y_1,x_2})的区间和(+{x_2,y_2})的最大前缀和(()上图绿色线段())

    此题还有两个需要注意的地方(:)

    • 当查询到(l > r)时要返回空节点,否则会像我一样(RE)到死(……)

    • 此题的最大子段和没有要求不能为空,所以我们在每次更新时都需要和(0)进行比较。

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include<bits/stdc++.h>
    
    namespace LXC
    {
        template<class T>
        inline T read(T &x)
        {
            x = 0; int w = 1, ch = getchar();
            while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
            while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
            return x *= w;
        }
    
        template<class T>
        inline T max(const T a, const T b)
        {
            return a > b ? a : b;
        }
    }
    using namespace LXC;
    
    const int maxn = 1e4 + 5;
    int T, n, m, a[maxn];
    
    struct Node {
        int l, r, sum, lsum, rsum, maxsum;
    }z[maxn << 2], nmsl;
    
    Node operator + (const Node &a, const Node &b)
    {
        Node ans;
        ans.l = a.l;
        ans.r = b.r;
        ans.lsum = max(a.lsum, a.sum + b.lsum);
        ans.rsum = max(b.rsum, b.sum + a.rsum);
        ans.maxsum = max(max(a.maxsum, b.maxsum), a.rsum + b.lsum);
        ans.sum = a.sum + b.sum;
        return ans;
    }
    
    void build(int rt, int l, int r)
    {
        z[rt].l = l, z[rt].r = r;
        if (l == r) {
            z[rt].sum = z[rt].lsum = z[rt].rsum = z[rt].maxsum = a[l];
            return ;
        }
        int mid = (l + r) >> 1;
        build(rt << 1, l, mid);
        build(rt << 1 | 1, mid + 1, r);
        z[rt] = z[rt << 1] + z[rt << 1 | 1];
    }
    
    Node query(int rt, int l, int r)
    {
        if (l > r) return nmsl;
        if (l == z[rt].l && z[rt].r == r) return z[rt];
        int mid = (z[rt].l + z[rt].r) >> 1;
        if (r <= mid)
            return query(rt << 1, l, r);
        if (l > mid)
            return query(rt << 1 | 1, l, r);
        return query(rt << 1, l, mid) + query(rt << 1 | 1, mid + 1, r);
    }
    
    int main()
    {
        read(T);
        while (T--) {
            read(n);
            for (int i = 1; i <= n; i++) read(a[i]);
            build(1, 1, n);
            read(m);
            int x1, y1, x2, y2;
            for (int i = 1; i <= m; i++) {
                read(x1), read(y1), read(x2), read(y2);
                if (y1 < x2) {
                    printf("%d
    ", max(query(1, x1, y1 - 1).rsum, 0) + query(1, y1, x2).sum + max(query(1, x2 + 1, y2).lsum, 0));
                }
                else {
                    int ans = query(1, x2, y1).maxsum;
                    ans = max(ans, query(1, x1, x2 - 1).rsum + query(1, x2, y1).lsum);
                    ans = max(ans, query(1, x2, y1).rsum + query(1, y1 + 1, y2).lsum);
                    ans = max(ans, max(query(1, x1, x2 - 1).rsum, 0) + query(1, x2, y1).sum + max(0, query(1, y1 + 1, y2).lsum));
                    printf("%d
    ", ans);
                }
            }
        }
        return 0;
    }
    
    

    GSS 6

    题目要求:插入元素,删除元素,单点修改,查询最大子段和。

    很明显看到插入操作和删除操作,我们就知道这题要用平衡树,本人使用(fhq~Treap)来实现这些操作。

    一些需要注意的点(:)

    • 在进行(split)时,我们习惯上采用按照权值来分,但这道题在哪个位置就把另设的一个权值标上位置,所以我们直接按照点的(size)来分。

    • 由于是平衡树,我们需要考虑左右子树是否为空的情况,所以我们采取常用的分类讨论的方式,唯一一点就是这样写下来你的(update)函数可能会巨长(……)

    • 本题我们用平衡树,因此在维护一个节点时,会分成左子树,自己,右子树三个部分,和线段树略有不同。

    • 这个题需要的空间很大,推荐直接开(4)倍。

    • 十年(OI)一场空,不开(long~long)见祖宗!!

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <ctime>
    #define LL long long
    
    namespace LXC
    {
        template<class T>
        inline T read(T &x)
        {
            x = 0; int w = 1, ch = getchar();
            while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
            while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
            return x *= w;
        }
    
        template<class T>
        inline T max(const T a, const T b)
        {
            return a > b ? a : b;
        }
    }
    
    using namespace LXC;
    
    const int maxn = 4e5 + 5;
    int n, m;
    int l, r, _z, temp, opt;
    
    struct Node {
        int ls, rs, size, key;
        LL val, sum, lsum, rsum, maxsum;
    }z[maxn];
    int root, cnt;
    
    inline void update(int rt)
    {
        z[rt].sum = z[rt].val, z[rt].size = 1;
        if (z[rt].ls)
            z[rt].sum += z[z[rt].ls].sum, z[rt].size += z[z[rt].ls].size;
        if (z[rt].rs)
            z[rt].sum += z[z[rt].rs].sum, z[rt].size += z[z[rt].rs].size;
        if (z[rt].ls && z[rt].rs) {
            z[rt].lsum = max(z[z[rt].ls].lsum, z[z[rt].ls].sum + z[rt].val + z[z[rt].rs].lsum);
            z[rt].rsum = max(z[z[rt].rs].rsum, z[z[rt].rs].sum + z[rt].val + z[z[rt].ls].rsum);
            z[rt].maxsum = max(z[z[rt].ls].maxsum, max(z[z[rt].rs].maxsum, z[z[rt].ls].rsum + z[z[rt].rs].lsum + z[rt].val));
        }
        else if (z[rt].ls) {
            z[rt].lsum = max(max(z[z[rt].ls].lsum, z[z[rt].ls].sum + z[rt].val), 0ll);
            z[rt].rsum = max(z[z[rt].ls].rsum + z[rt].val, 0ll);
            z[rt].maxsum = max(z[z[rt].ls].maxsum, z[z[rt].ls].rsum + z[rt].val);
        }
        else if (z[rt].rs) {
            z[rt].lsum = max(z[z[rt].rs].lsum + z[rt].val, 0ll);
            z[rt].rsum = max(max(z[z[rt].rs].rsum, z[z[rt].rs].sum + z[rt].val), 0ll);
            z[rt].maxsum = max(z[z[rt].rs].maxsum, z[z[rt].rs].lsum + z[rt].val);
        }
        else {
            z[rt].lsum = z[rt].rsum = max(z[rt].val, 0ll);
            z[rt].maxsum = z[rt].val;
        }
    }
    
    inline int insert(int x)
    {
        ++cnt;
        z[cnt].size = 1;
        z[cnt].key = rand();
        z[cnt].val = z[cnt].sum = z[cnt].maxsum = x;
        z[cnt].lsum = z[cnt].rsum = max(x, 0);
        return cnt;
    }
    
    inline int merge(int x, int y)
    {
        if (!x || !y) return x + y;
        if (z[x].key < z[y].key) {
            z[x].rs = merge(z[x].rs, y);
            update(x);
            return x;
        }
        else {
            z[y].ls = merge(x, z[y].ls);
            update(y);
            return y;
        }
    }
    
    inline void split(int rt, int x, int &l, int &r)
    {
        if (!rt) {l = r = 0; return ;}
        if (x <= z[z[rt].ls].size) {
            r = rt, split(z[rt].ls, x, l, z[rt].ls);
        }
        else {
            l = rt, split(z[rt].rs, x - z[z[rt].ls].size - 1, z[rt].rs, r);
        }
        update(rt);
    }
    
    int main()
    {
        char ch;
        srand((unsigned)time(NULL));
        read(n);
        for (int i = 1; i <= n; i++) {
            read(temp);
            split(root, i, l, r);
            root = merge(merge(l, insert(temp)), r);
        }
        read(m);
        for (int i = 1; i <= m; i++) {
            std::cin >> ch;
            if (ch == 'I') {
                read(opt), read(temp);
                split(root, opt - 1, l, r);
                root = merge(merge(l, insert(temp)), r);
            }
            else if (ch == 'D') {
                read(opt);
                split(root, opt, l, _z);
                split(l, opt - 1, l, r);
                r = merge(z[r].ls, z[r].rs);
                root = merge(merge(l, r), _z);
            }
            else if (ch == 'R') {
                read(opt), read(temp);
                split(root, opt, l, _z);
                split(l, opt - 1, l, r);
                z[r].val = temp;
                z[r].sum = z[r].maxsum = temp;
                z[r].lsum = z[r].rsum = max(temp, 0);
                root = merge(merge(l, r), _z);
            }
            else {
                read(opt), read(temp);
                split(root, temp, l, _z);
                split(l, opt - 1, l, r);
                printf("%lld
    ", z[r].maxsum);
                root = merge(merge(l, r), _z);
            }
        }
        return 0;
    }
    
    

    GSS 7

    (GSS)系列唯一一道树上的题目,虽然仅仅是将操作挪到了树上,但代码的难度确实是倍增(……)

    考虑到我们的操作是在树上,所以我们首先用树链剖分将它映射到序列上,然后对序列进行操作。

    本人觉得这道题的难点在于询问操作上,其他的操作都很常规。

    关于询问操作(:)

    • 考虑到两个点都在向他们的(LCA)跳,我们分别记录两个答案(L,R),最后返回答案时,再用重载后的加法将(L)(R)合并起来。

    • 再我们进行最后一步合并前,我们需要交换一下(L)的最大前缀和和最大后缀和。不太明白? 看图(:)

    例如,我们要求(9)号节点到(14)号节点这条链上的最大子段和,当这两个点跳到他们的(LCA)(1)号节点时进行合并操作,但我们发现,这时(L)的最大前缀和和(R)的最大后缀和连在了一起,如果这时合并,很明显是错的,所以我们需要将(L)的最大前缀和和最大后缀和交换一下。

    然后,注意一下代码细节和常数问题就好了。

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    namespace LXC
    {
        template<class T>
        inline T read(T &x)
        {
            x = 0;
            int w = 1, ch = getchar();
            while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
            while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
            return x *= w;
        }
    
        template<class T>
        inline T max(const T a, const T b)
        {
            return a > b ? a :b;
        }
    }
    
    using namespace LXC;
    
    const int maxn = 1e5 + 5;
    int n, m, val[maxn], head[maxn], tot;
    int dep[maxn], size[maxn], fa[maxn], son[maxn], top[maxn], idx[maxn], a[maxn], num, cnt;
    
    struct Node {
        int sum, lsum, rsum, maxsum, tag;
        bool cov;
        Node(){sum = lsum = rsum = maxsum = 0;}
    }z[maxn << 2];
    
    struct Edge {
        int to, nxt;
    }edge[maxn << 1];
    
    void add(int from, int to)
    {
        edge[++tot].to = to;
        edge[tot].nxt = head[from];
        head[from] = tot;
    }
    
    void dfs1(int u, int f, int deep)
    {
        dep[u] = deep;
        fa[u] = f;
        size[u] = 1;
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].to;
            if (v == f) continue;
            dfs1(v, u, deep + 1);
            size[u] += size[v];
            if (size[v] > size[son[u]]) son[u] = v;
        }
    }
    
    void dfs2(int u, int topf)
    {
        top[u] = topf;
        idx[u] = ++num;
        a[num] = val[u];
        if (!son[u]) return ;
        dfs2(son[u], topf);
        for (int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].to;
            if (v == fa[u] || v == son[u]) continue;
            dfs2(v, v);
        }
    }
    
    Node operator + (const Node &a, const Node &b)
    {
        Node ans;
        ans.sum = a.sum + b.sum;
        ans.lsum = max(a.lsum, a.sum + b.lsum);
        ans.rsum = max(b.rsum, a.rsum + b.sum);
        ans.maxsum = max(max(a.maxsum, b.maxsum), a.rsum + b.lsum);
        ans.tag = ans.cov = 0;
        return ans;
    }
    
    void build(int rt, int l, int r)
    {
        if (l == r) {
            z[rt].sum = a[l];
            z[rt].lsum = z[rt].rsum = z[rt].maxsum = max(z[rt].sum, 0);
            z[rt].cov = 0;
            return ;
        }
        int mid = (l + r) >> 1;
        build(rt << 1, l, mid);
        build(rt << 1 | 1, mid + 1, r);
        z[rt] = z[rt << 1] + z[rt << 1 | 1];
    }
    
    void update(int rt, int l, int r, int k)
    {
        z[rt].sum = (r - l + 1) * k;
        z[rt].lsum = z[rt].rsum = z[rt].maxsum = max(z[rt].sum, 0);
        z[rt].tag = k, z[rt].cov = 1;
    }
    
    void pushdown(int rt, int l, int r)
    {
        if (!z[rt].cov) return ;
        int mid = (l + r) >> 1;
        update(rt << 1, l, mid, z[rt].tag);
        update(rt << 1 | 1, mid +1, r, z[rt].tag);
        z[rt].tag = z[rt].cov = 0;
    }
    
    void modify(int rt, int x, int y, int l, int r, int k)
    {
        if (x <= l && r <= y) {
            update(rt, l, r, k);
            return ;
        }
        pushdown(rt, l, r);
        int mid = (l + r) >> 1;
        if (x <= mid)
            modify(rt << 1, x, y, l, mid, k);
        if (y > mid)
            modify(rt << 1 | 1, x, y, mid + 1, r, k);
        z[rt] = z[rt << 1] + z[rt << 1 | 1];
    }
    
    Node query(int rt, int x, int y, int l, int r)
    {
        Node L, R;
        if (x <= l && r <= y)
            return z[rt];
        pushdown(rt, l, r);
        int mid = (l + r) >> 1;
        if (x <= mid)
            L = query(rt << 1, x, y, l, mid);
        if (mid < y)
            R = query(rt << 1 | 1, x, y, mid + 1, r);
        return L + R;
    }
    
    void uprange(int x, int y, int k)
    {
        while (top[x] != top[y]) {
            if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
            modify(1, idx[top[x]], idx[x], 1, n, k);
            x = fa[top[x]];
        }
        if (dep[x] > dep[y]) std::swap(x, y);
        modify(1, idx[x], idx[y], 1, n, k);
    }
    
    Node range(int x, int y)
    {
        Node L, R;
        while (top[x] != top[y]) {
            if (dep[top[x]] < dep[top[y]]) {
                R = query(1, idx[top[y]], idx[y], 1, n) + R;
                y = fa[top[y]];
            }
            else {
                L = query(1, idx[top[x]], idx[x], 1, n) + L;
                x = fa[top[x]];
            }
        }
        if (dep[x] <= dep[y])
            R = query(1, idx[x], idx[y], 1, n) + R;
        else
            L = query(1, idx[y], idx[x], 1, n) + L;
        std::swap(L.lsum, L.rsum);
        return L + R;
    }
    
    int main()
    {
        int x, y;
        read(n);
        for (int i = 1; i <= n; i++) read(val[i]);
        for (int i = 1; i <= n - 1; i++) {
            read(x), read(y);
            add(x, y), add(y, x);
        }
        dfs1(1, 0, 1);
        dfs2(1, 1);
        build(1, 1, n);
        read(m);
        int opt, u, v, w;
        while (m--) {
            read(opt);
            if (opt == 1) {
                read(u), read(v);
                Node ans = range(u, v);
                printf("%d
    ", ans.maxsum);
            }
            else {
                read(u), read(v), read(w);
                uprange(u, v, w);
            }
        }
        return 0;
    }
    

    同样出自某钟姓神仙之口:“(GSS)系列本来只有(7)道题,第八道是后面人又加上的。”

    再加上蒟蒻最近写数据结构有点恶心,预计将会在
    天后将会更新(GSS8.)

    咕咕咕(……)

  • 相关阅读:
    D. Babaei and Birthday Cake--- Codeforces Round #343 (Div. 2)
    Vijos P1389婚礼上的小杉
    AIM Tech Round (Div. 2) C. Graph and String
    HDU 5627Clarke and MST
    bzoj 3332 旧试题
    codeforces 842C Ilya And The Tree
    codesforces 671D Roads in Yusland
    Travelling
    codeforces 606C Sorting Railway Cars
    codeforces 651C Watchmen
  • 原文地址:https://www.cnblogs.com/Hydrogen-Helium/p/11737195.html
Copyright © 2011-2022 走看看