kuangbin专题七线段树总结

总结:线段树最重要的就是pushup和pushdown两个函数。此外,根据题目需要,要清楚自己要维护的东西。
D - Mayor’s posters
这一题,首先x的范围很大,所以要先离散化,离散化之后,每两个点之间如果坐标值之差大于1的话,他们中间还得插一个值。否则的话,读者可以试试这组数据
3
5 6
4 5
6 8
如果没有插的话就会输出2了。
线段树维护区间是否被填满。

#include<iostream>
#include<cstring>
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 200050;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int all[maxn << 4];
void PushUp(int rt) { if (all[rt << 1] && all[rt << 1 | 1])all[rt] = 1; }



bool Update(int L, int R, int l, int r, int rt)
{
    if (all[rt])return 0;
    if (L <= l&&r <= R)
    {
        all[rt] = 1;
        return 1;
    }
    //Pushdown(rt);
    bool ans=0;
    int m = l + r >> 1;
    if (L <= m)ans =ans|Update(L, R, ls);
    if (R > m)
        ans = ans | Update(L, R, rs);
    PushUp(rt);
    return ans;
}
vector<int>V;
int Left[maxn], Right[maxn];
int Hash[10000005];
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        memset(all, 0, sizeof(all));

        V.clear();
        int n;
        scanf("%d", &n);
        //int l, r;
        for (int i = 1;i <= n;i++)
        {
            scanf("%d %d", &Left[i], &Right[i]);
            V.push_back(Left[i]);
            V.push_back(Right[i]);
        }
        sort(V.begin(), V.end());
        int thesize=unique(V.begin(), V.end())-V.begin();
        for (int i = 1;i < thesize;i++)
        {
            if (V[i] - V[i - 1] > 1)V.push_back(V[i] - 1);
        }
        sort(V.begin(), V.end());
        thesize=unique(V.begin(), V.end())-V.begin();
        for (int i = 0;i < thesize;i++)
            Hash[V[i]] = i;
        int ans = 0;
        for (int i = n;i >= 1;i--)
        {
            int lidx = Hash[Left[i]]+1;
            int ridx = Hash[Right[i]]+1;
            if (Update(lidx, ridx, 1, thesize, 1))
                ans++;
        }
        cout << ans << endl;
    }
    return 0;
}

H - Can you answer these queries?
这题有个隐藏的信息,一个不大于2^63的数,顶多开方7次,7次之后就会变成1了。
所以我们可以维护区间和,判断区间和和区间长度的关系来看是否要点更新。

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<math.h>
using namespace std;
const int maxn = 100005;
typedef long long ll;
ll sum[maxn << 2], A[maxn];
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

void pushup(int rt) { sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; }
int n;
void build(int l, int r, int rt)
{
    if (l == r) { sum[rt] = A[l];return; }
    int m = l + r >> 1;
    build(ls);
    build(rs);
    pushup(rt);
}
void update(int L, int l, int r, int rt)
{
    if (l == r)
    {
        sum[rt] = sqrt(sum[rt]);
        A[L] = sqrt(A[L]);
        return;
    }
    int m = l + r >> 1;
    if (L <= m)update(L, ls);
    else update(L, rs);
    pushup(rt);
}
void update1(int L, int R, int l, int r, int rt)
{
    if (L <= l&&r <= R)
    {
        if (sum[rt] == r - l + 1)return;
        for (int i = l;i <= r;i++)
        {
            if (A[i] == 1)continue;
            update(i, 1, n, 1);
        }
        return;
    }

    int m = l + r >> 1;
    if (L <= m)
        update1(L, R, ls);
    if (R > m)
        update1(L, R, rs);
    pushup(rt);
}

ll query(int L, int R, int l, int r, int rt)
{
    if (L <= l&&r <= R)
        return sum[rt];
    int m = l + r >> 1;
    ll ans = 0;
    if (L <= m)ans += query(L, R, ls);
    if (R > m)ans += query(L, R, rs);
    return ans;
}


int main()
{
    int cas = 1;
    while (~scanf("%d",&n))
    {

        for (int i = 1;i <= n;i++)
            scanf("%lld", &A[i]);
        build(1, n, 1);
        int q;
        scanf("%d", &q);
        int opt, x, y;
        printf("Case #%d:\n", cas++);
        while (q--)
        {

            scanf("%d %d %d", &opt, &x, &y);
            if (x > y)swap(x, y);
            if (opt == 0)
                update1(x, y, 1, n, 1);
            else
                printf("%lld\n", query(x, y, 1, n, 1));

        }
        puts("");
    }
    return 0;
}

L - Vases and Flowers
根据题意,我们可以维护区间和然后二分找第一个放花的地方和最后一个放花的地方。
做完这题,终于会二分了。。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 50005;
int n, mn;
int sum[maxn << 2], lazy[maxn << 2];
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

void pushup(int rt)
{
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushdown(int rt, int ln, int rn)
{
    if (lazy[rt] != -1)
    {
        sum[rt << 1] = ln*lazy[rt];
        sum[rt << 1 | 1] = rn*lazy[rt];
        lazy[rt << 1] = lazy[rt];
        lazy[rt << 1 | 1] = lazy[rt];
        lazy[rt] = -1;
    }
}

void build(int l, int r, int rt)
{

    if (l == r)
    {
        sum[rt] = 1;
        lazy[rt] = -1;
        return;
    }
    int m = l + r >> 1;
    build(ls);
    build(rs);
    pushup(rt);
}

void update(int L, int R, int C, int l, int r, int rt)
{
    if (L <= l&&r <= R)
    {
        sum[rt] = (r - l + 1)*C;
        lazy[rt] = C;
        return;
    }
    int m = l + r >> 1;
    pushdown(rt, m - l + 1, r - m);
    if (L <= m)
        update(L, R, C, ls);
    if (R > m)
        update(L, R, C, rs);
    pushup(rt);
}

int query(int L, int R, int l, int r, int rt)
{
    if (L <= l&&r <= R)
        return sum[rt];
    int m = l + r >> 1;
    pushdown(rt, m - l + 1, r - m);
    int ans = 0;
    if (L <= m)
        ans += query(L, R, ls);
    if (R > m)
        ans += query(L, R, rs);
    return ans;
}

int queryleft(int x, int y)//yes
{
    int l = x, r = n - 1, mid;
    int tmp;
    int ans = -1;
    while (l <= r)
    {
        mid = l + r >> 1;
        tmp = query(mid, n - 1, 0, n - 1, 1);
        if (tmp < y)
            r = mid - 1;
        else
            l = mid + 1, ans = mid;
    }
    return ans;
}

int queryright(int left, int x, int y)
{
    int l = left, r = n - 1, mid;
    int tmp;
    int ans = -1;
    while (l <= r)
    {
        mid = l + r >> 1;
        tmp = query(left, mid, 0, n - 1, 1);
        if (tmp < y)
            l = mid + 1;
        else
            r = mid - 1;
        if (tmp == y)
            ans = mid;
    }
    return ans;
}


void solve(int x, int y)
{
    int tmp = query(x, n, 0, n - 1, 1);
    if (tmp == 0 || y == 0)
    {
        puts("Can not put any one.");
        return;
    }
    y = min(y, tmp);
    int left = queryleft(x, tmp);
    int right = queryright(left, x, y);
    update(left, right, 0, 0, n - 1, 1);
    printf("%d %d\n", left, right);
}


int main()
{
    int T;
    scanf("%d", &T);
    int flag = 1;
    while (T--)
    {
        memset(lazy, -1, sizeof(lazy));
        scanf("%d %d", &n, &mn);
        build(0, n - 1, 1);
        int opt, x, y;
        while (mn--)
        {
            scanf("%d %d %d", &opt, &x, &y);
            if (opt == 1)
                solve(x, y);
            else
            {
                int ans = query(x, y, 0, n - 1, 1);
                ans = (y - x + 1) - ans;
                update(x, y, 1, 0, n - 1, 1);
                printf("%d\n", ans);
            }
            /*for (int i = 0;i < n;i++)
            printf("%d:%d\n", i, query(i, i, 0, n-1, 1));*/
        }
        puts("");
    }
    return 0;
}

M - 约会安排
我们可以开两个线段树,一个是维护屌丝未使用的最长连续时间,一个是维护女神未使用的最长连续时间。那么更新时候怎么更新呢。
如果是屌丝找的话,那么只看屌丝的线段树。如果是女神找的话,我们优先看未使用的时间段,所以先看屌丝的线段树,如果有的话,那么更新。如果没有的话,再看女神的线段树。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100005;
struct node
{
    int lds, rds, lns, rns, maxds, maxns;
    int lazyds, lazyns;
    node(int _ld, int _rd, int _ln, int _rn, int _ds, int _ns, int _lds, int _lns) :lds(_ld), rds(_rd), lns(_ln), rns(_rn), maxds(_ds), maxns(_ns), lazyds(_lds), lazyns(_lns) {}
    node() {}
}nodes[maxn << 2];
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

void pushup(int rt, int l, int r)
{
    nodes[rt].lds = nodes[rt << 1].lds;
    nodes[rt].rds = nodes[rt << 1 | 1].rds;
    int m = l + r >> 1;
    if (nodes[rt << 1].lds == m - l + 1)
        nodes[rt].lds += nodes[rt << 1 | 1].lds;
    if (nodes[rt << 1 | 1].rds == r - m)
        nodes[rt].rds += nodes[rt << 1].rds;

    nodes[rt].maxds = max(nodes[rt << 1].maxds, nodes[rt << 1 | 1].maxds);
    nodes[rt].maxds = max(nodes[rt].maxds, nodes[rt << 1].rds + nodes[rt << 1 | 1].lds);

    nodes[rt].lns = nodes[rt << 1].lns;
    nodes[rt].rns = nodes[rt << 1 | 1].rns;
    if (nodes[rt << 1].maxns == m - l + 1)
        nodes[rt].lns += nodes[rt << 1 | 1].lns;
    if (nodes[rt << 1 | 1].maxns == r - m)
        nodes[rt].rns += nodes[rt << 1].rns;

    nodes[rt].maxns = max(nodes[rt << 1].maxns, nodes[rt << 1 | 1].maxns);
    nodes[rt].maxns = max(nodes[rt].maxns, nodes[rt << 1].rns + nodes[rt << 1 | 1].lns);

}

void build(int l, int r, int rt)
{
    nodes[rt] = node(r - l + 1, r - l + 1, r - l + 1, r - l + 1, r - l + 1, r - l + 1, 0, 0);
    if (l == r)return;
    int m = l + r >> 1;
    build(ls);
    build(rs);
}

void pushdown(int rt, int ln, int rn)
{
    if (nodes[rt].lazyds)
    {
        if (nodes[rt].lazyds == 1)
        {
            nodes[rt << 1].lds = 0;
            nodes[rt << 1].rds = 0;
            nodes[rt << 1].maxds = 0;
            nodes[rt << 1].lazyds = 1;
            nodes[rt << 1 | 1].lds = 0;
            nodes[rt << 1 | 1].rds = 0;
            nodes[rt << 1 | 1].maxds = 0;
            nodes[rt << 1 | 1].lazyds = 1;
        }
        else
        {
            nodes[rt << 1].lds = ln;
            nodes[rt << 1].rds = ln;
            nodes[rt << 1].maxds = ln;
            nodes[rt << 1].lazyds = 2;
            nodes[rt << 1 | 1].lds = rn;
            nodes[rt << 1 | 1].rds = rn;
            nodes[rt << 1 | 1].maxds = rn;
            nodes[rt << 1 | 1].lazyds = 2;
        }
        nodes[rt].lazyds = 0;
    }

    if (nodes[rt].lazyns)
    {
        if (nodes[rt].lazyns == 1)
        {
            nodes[rt << 1].lns = 0;
            nodes[rt << 1].rns = 0;
            nodes[rt << 1].maxns = 0;
            nodes[rt << 1].lazyns = 1;
            nodes[rt << 1 | 1].lns = 0;
            nodes[rt << 1 | 1].rns = 0;
            nodes[rt << 1 | 1].maxns = 0;
            nodes[rt << 1 | 1].lazyns = 1;
        }
        else
        {
            nodes[rt << 1].lns = ln;
            nodes[rt << 1].rns = ln;
            nodes[rt << 1].maxns = ln;
            nodes[rt << 1].lazyns = 2;
            nodes[rt << 1 | 1].lns = rn;
            nodes[rt << 1 | 1].rns = rn;
            nodes[rt << 1 | 1].maxns = rn;
            nodes[rt << 1 | 1].lazyns = 2;
        }
        nodes[rt].lazyns = 0;
    }
}


void update(int type, int L, int R, int C, int l, int r, int rt)
{
    if (L <= l&&r <= R)
    {
        if (type == 1)
        {
            if (C == 1)
            {
                nodes[rt].lds = 0;
                nodes[rt].rds = 0;
                nodes[rt].maxds = 0;
                nodes[rt].lazyds = 1;
            }
            else
            {
                nodes[rt].lds = r - l + 1;
                nodes[rt].rds = r - l + 1;
                nodes[rt].maxds = r - l + 1;
                nodes[rt].lazyds = 2;
            }
        }
        else
        {
            if (C == 1)
            {
                nodes[rt].lns = 0;
                nodes[rt].rns = 0;
                nodes[rt].maxns = 0;
                nodes[rt].lazyns = 1;
            }
            else
            {
                nodes[rt].lns = r - l + 1;
                nodes[rt].rns = r - l + 1;
                nodes[rt].maxns = r - l + 1;
                nodes[rt].lazyns = 2;
            }
        }
        return;
    }
    int m = l + r >> 1;
    pushdown(rt, m - l + 1, r - m);
    if (L <= m)
        update(type, L, R, C, ls);
    if (R > m)
        update(type, L, R, C, rs);
    pushup(rt, l, r);
}

int query(int C, int type, int l, int r, int rt)
{
    if (l == r)return l;
    int m = l + r >> 1;
    pushdown(rt, m - l + 1, r - m);
    if (type == 1)
    {
        if (nodes[rt << 1].maxds >= C)
            return query(C, type, ls);
        else if (nodes[rt << 1].rds + nodes[rt << 1 | 1].lds >= C)
            return m - nodes[rt << 1].rds + 1;
        else
            return query(C, type, rs);
    }
    else
    {
        if (nodes[rt << 1].maxns >= C)
            return query(C, type, ls);
        else if (nodes[rt << 1].rns + nodes[rt << 1 | 1].lns >= C)
            return m - nodes[rt << 1].rns + 1;
        else
            return query(C, type, rs);
    }
}

int query2(int L, int l, int r, int rt)
{
    if (l == r)return nodes[rt].lds;
    int m = l + r >> 1;
    pushdown(rt, m - l + 1, r - m);
    if (L <= m)return query2(L, ls);
    else
        return query2(L, rs);
}

int query3(int L, int l, int r, int rt)
{
    if (l == r)return nodes[rt].lns;
    int m = l + r >> 1;
    pushdown(rt, m - l + 1, r - m);
    if (L <= m)return query3(L, ls);
    else
        return query3(L, rs);
}

int main()
{
    int T;
    scanf("%d", &T);
    int cas = 1;
    while (T--)
    {
        printf("Case %d:\n", cas++);
        int n, m;
        scanf("%d %d", &n, &m);
        build(1, n, 1);
        char s[10];
        int x, y;
        for (int i = 1;i <= m;i++)
        {
            scanf("%s %d", s, &x);
            if (s[0] == 'D')
            {
                int time = nodes[1].maxds;
                if (time < x)
                    puts("fly with yourself");
                else
                {
                    int q = query(x, 1, 1, n, 1);
                    update(1, q, q + x - 1, 1, 1, n, 1);
                    printf("%d,let's fly\n", q);
                }
            }
            else if (s[0] == 'N')
            {
                int time = nodes[1].maxds;
                if (time < x)
                {
                    if (x > nodes[1].maxns)
                        puts("wait for me");
                    else
                    {
                        int q = query(x, 2, 1, n, 1);
                        update(2, q, q + x - 1, 1, 1, n, 1);
                        update(1, q, q + x - 1, 1, 1, n, 1);
                        printf("%d,don't put my gezi\n", q);
                    }
                }
                else
                {
                    int q = query(x, 1, 1, n, 1);
                    update(1, q, q + x - 1, 1, 1, n, 1);
                    update(2, q, q + x - 1, 1, 1, n, 1);
                    printf("%d,don't put my gezi\n", q);
                }
            }
            else
            {
                scanf("%d", &y);
                update(1, x, y, 0, 1, n, 1);
                update(2, x, y, 0, 1, n, 1);
                puts("I am the hope of chinese chengxuyuan!!");
            }
            /*for (int j = 1;j <= n;j++)
            printf("%d:%d %d\n", j, query2(j, 1, n, 1),query3(j,1,n,1));*/
        }
    }
    return 0;
}

这题主要是线段树维护最长连续区间的模板。
Q - Get The Treasury
题意:给了三维坐标系中n个长方体,求重叠大于2次的体积。
我们可以将体积分成若干个高为1的长方体来算,那么就转换成求面积了。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<map>
using namespace std;
const int maxn = 1005;
int X[maxn << 1];
struct line
{
    int l, r, h;
    int flag;

    line(int _l,int _r,int _h,int _flag):l(_l),r(_r),h(_h),flag(_flag){}

    line(){}

    bool operator<(const line &b)const
    {
        return h < b.h;
    }
}lines[1005][maxn << 1];
struct node
{
    int l, r;
    int cnt;
    int len1, len2, len3;
    node(int _l, int _r, int _c, int _l1, int _l2, int _l3) :l(_l), r(_r), cnt(_c), len1(_l1), len2(_l2), len3(_l3){}
    node(){}
}nodes[maxn << 3];

#define ls l,m,rt<<1    
#define rs m+1,r,rt<<1|1

void build(int l, int r, int rt)
{
    nodes[rt] = node(l, r, 0, 0, 0, 0);
    if (l == r)return;
    int m = l + r >> 1;
    build(ls);
    build(rs);
}

void pushup(int rt)
{
    int lidx = nodes[rt].l;
    int ridx = nodes[rt].r + 1;
    if (nodes[rt].cnt)
        nodes[rt].len1 = X[ridx] - X[lidx];
    else if (nodes[rt].l == nodes[rt].r)
        nodes[rt].len1 = 0;
    else
        nodes[rt].len1 = nodes[rt << 1].len1 + nodes[rt << 1 | 1].len1;

    if (nodes[rt].cnt > 1)
        nodes[rt].len2 = X[ridx] - X[lidx];
    else if (nodes[rt].l == nodes[rt].r)
        nodes[rt].len2 = 0;
    else
        if (nodes[rt].cnt == 1)
        {
            nodes[rt].len2 = nodes[rt << 1].len1 + nodes[rt << 1 | 1].len1;
        }
        else
            nodes[rt].len2 = nodes[rt << 1].len2 + nodes[rt << 1 | 1].len2;

    if (nodes[rt].cnt > 2)
        nodes[rt].len3 = X[ridx] - X[lidx];
    else if (nodes[rt].l == nodes[rt].r)
        nodes[rt].len3 = 0;
    else
    {
        if (nodes[rt].cnt == 2)
        {
            nodes[rt].len3 = nodes[rt << 1].len1 + nodes[rt << 1 | 1].len1;
        }
        else if (nodes[rt].cnt == 1)
        {
            nodes[rt].len3 = nodes[rt << 1].len2 + nodes[rt << 1 | 1].len2;
        }
        else
            nodes[rt].len3 = nodes[rt << 1].len3 + nodes[rt << 1 | 1].len3;
    }
}

void update(int L, int R, int C, int l, int r, int rt)
{
    if (L <= l&&r <= R)
    {
        nodes[rt].cnt += C;
        pushup(rt);
        return;
    }
    int m = l + r >> 1;
    if (L <= m)
        update(L, R, C, ls);
    if (R > m)
        update(L, R, C, rs);
    pushup(rt);
}
typedef long long ll;

int main()
{
    int T;
    scanf("%d", &T);
    int cas = 1;
    while (T--)
    {
        int n;
        scanf("%d", &n);
        int x1, y1, z1, x2, y2, z2;
        int lnum[1005];
        memset(lnum, 0, sizeof(lnum));
        int xnum=0;
        for (int i = 1;i <= n;i++)
        {
            scanf("%d %d %d %d %d %d", &x1, &y1, &z1, &x2, &y2, &z2);
            for (int j = z1+500;j < z2+500;j++)
            {
                lines[j][lnum[j]++] = line(x1, x2, y1, 1);
                lines[j][lnum[j]++] = line(x1, x2, y2, -1);
            }
            X[xnum++] = x1;
            X[xnum++] = x2;
        }

        for (int i = 0;i <= 1000;i++)
            sort(lines[i], lines[i] + lnum[i]);
        sort(X, X + xnum);
        xnum = unique(X, X + xnum) - X;
        ll ans = 0;
        for (int i = 0;i <= 1000;i++)
        {
            build(0, xnum - 1, 1);
            for (int j = 0;j < lnum[i];j++)
            {
                int lidx = lower_bound(X, X + xnum, lines[i][j].l) - X;
                int ridx = lower_bound(X, X + xnum, lines[i][j].r) - X;
                ridx--;
                update(lidx, ridx, lines[i][j].flag, 0, xnum - 1, 1);
                ans += 1LL * nodes[1].len3*(lines[i][j + 1].h - lines[i][j].h);
            }
            //cout << ans << endl;
        }

        printf("Case %d: %lld\n", cas++, ans);
    }
    return 0;
}

仔细体会一下这个pushup。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值