线段树复习--[kuangbin带你飞] 线段树

本文深入探讨了线段树在不同场景下的应用案例,包括区间更新、离散化处理、区间查询等核心功能。通过典型问题如“简单整数问题”、“市长的海报”等,详细介绍了线段树的基本原理及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

温故知新,现在代码水平好了一些,重新刷kuangbin的专题也不那么费劲了。争取理解的透彻一些吧。

区间更新模板:

LL num[maxn],sum[maxn<<2],mark[maxn<<2];

void push_up(int root) {
    sum[root] = sum[root<<1]+sum[root<<1|1];
}
void push_down(int root,int cl,int cr) {
    if(mark[root]) {
        mark[root<<1] += mark[root];
        mark[root<<1|1] += mark[root];
        sum[root<<1] += mark[root]*cl;
        sum[root<<1|1] += mark[root]*cr;
        mark[root] = 0;
    }
}

void build(int l,int r,int root) {  //建树
    if(l == r) {
        sum[root] = num[l];
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,root<<1);
    build(m+1,r,root<<1|1);  //注意m要+1
    push_up(root);
}

void update(int L,int R,LL x,int l,int r,int root) { //成段更新
    if(L <= l && r <= R) {
        mark[root] += x;  //延迟更新标记
        sum[root] += (r-l+1)*x;
        return ;
    }
    int m = (l+r)>>1;
    push_down(root,m-l+1,r-m);  //下推标记
    if(m >= L) update(L,R,x,l,m,root<<1);
    if(m < R) update(L,R,x,m+1,r,root<<1|1); //注意区间的开闭
    push_up(root);
}

LL query(int L,int R,int l,int r,int root) {  //区间查询
    if(L <= l && r <= R) {
        return sum[root];
    }
    int m = (l+r)>>1;
    if(mark[root]) push_down(root,m-l+1,r-m);
    LL ans = 0;
    if(m >= L) ans += query(L,R,l,m,root<<1);
    if(m < R) ans += query(L,R,m+1,r,root<<1|1);
    return ans;
}

C - A Simple Problem with Integers
题意:
裸的线段树成段更新。
即两种操作,成段更新,区间查询和

题解:
直接用线段树成段更新的方法即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 1e5+5;
LL mod = 1e9+7;
double eps = 0.00000001;
double PI = acos(-1);

LL num[maxn],sum[maxn<<2],mark[maxn<<2];

void push_up(int root) {
    sum[root] = sum[root<<1]+sum[root<<1|1];
}
void push_down(int root,int cl,int cr) {
    if(mark[root]) {
        mark[root<<1] += mark[root];
        mark[root<<1|1] += mark[root];
        sum[root<<1] += mark[root]*cl;
        sum[root<<1|1] += mark[root]*cr;
        mark[root] = 0;
    }
}

void build(int l,int r,int root) {
    if(l == r) {
        sum[root] = num[l];
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,root<<1);
    build(m+1,r,root<<1|1);
    push_up(root);
}

void update(int L,int R,LL x,int l,int r,int root) {
    if(L <= l && r <= R) {
        mark[root] += x;
        sum[root] += (r-l+1)*x;
        return ;
    }
    int m = (l+r)>>1;
    push_down(root,m-l+1,r-m);
    if(m >= L) update(L,R,x,l,m,root<<1);
    if(m < R) update(L,R,x,m+1,r,root<<1|1);
    push_up(root);
}

LL query(int L,int R,int l,int r,int root) {
    if(L <= l && r <= R) {
        return sum[root];
    }
    int m = (l+r)>>1;
    if(mark[root]) push_down(root,m-l+1,r-m);
    LL ans = 0;
    if(m >= L) ans += query(L,R,l,m,root<<1);
    if(m < R) ans += query(L,R,m+1,r,root<<1|1);
    return ans;
}

int main() {
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i = 1;i <= n;i++) scanf("%I64d",&num[i]);
    build(1,n,1);
    clr(mark,0);
    for(int i = 1;i <= q;i++) {
        char str[3];
        int l,r;LL x;
        scanf("%s",str);
        if(str[0] == 'Q') {
            scanf("%d%d",&l,&r);
            printf("%I64d\n",query(l,r,1,n,1));
        }
        else {
            scanf("%d%d%I64d",&l,&r,&x);
            update(l,r,x,1,n,1);
        }
    }
}

D - Mayor’s posters (离散化)

题意:
给你一个 1~1e7 的墙,给出一个n,对于每个 i = 1->n , 有两个端点 x1,x2 ,表示x1->x2有的范围有一张新的海报贴在墙上,范围是 x1-x2,问你贴完N个海报后墙上可以看到多少张海报,被完全覆盖的海报不算个数。

题解:
因为范围很大,先对坐标离散化,离散花的时候要注意如果两个坐标距离大于2,那么距离+2,不然是距离+1。
然后用线段树成端更新的作法。
最后我的做法是for循环从1到n都检查一遍,求海报数

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 1e5+5;
LL mod = 1e9+7;
double eps = 0.00000001;

pii num[maxn],st[maxn],st1[maxn];
int n,col[maxn],f[maxn],dd[maxn],cnt,cc;

void init() {
    clr(col,0);
    clr(f,0);
    cnt = 0;
    cc = 0;
}

void push_down(int root) {  //延迟更新
    col[root<<1] = col[root];
    col[root<<1|1] = col[root];
    f[root<<1] = f[root];
    f[root<<1|1] = f[root];
    f[root] = 0;
}

void update(int L,int R,int c,int l,int r,int root) { //成段更新
    if(L <= l && r <= R) {
        col[root] = c;
        f[root] = c;
        return ;
    }
    int m = (l+r)>>1;
    if(f[root]) push_down(root);
    if(m >= L) update(L,R,c,l,m,root<<1);
    if(m < R) update(L,R,c,m+1,r,root<<1|1);
}

int query(int pos,int l,int r,int root) {  //单点查询
    if(l == r && l == pos) {
        return col[root];
    }
    if(f[root]) push_down(root);
    int m = (l+r)>>1;
    if(m >= pos) query(pos,l,m,root<<1);
    else query(pos,m+1,r,root<<1|1);
}

bool cmp(pii a,pii b) {
    if(a.se == b.se) return a.fir < b.fir;
    return a.se < b.se;
}

int main() {
    int t;
    cin>>t;
    while(t--) {
        scanf("%d",&n);
        init();
        for(int i = 1;i <= n;i++) {  
            scanf("%d%d",&num[i].fir,&num[i].se);
            st[++cnt] = (make_pair(num[i].fir,i));
            st[++cnt] = (make_pair(num[i].se,i));
        }
        sort(st+1,st+1+cnt);
        st[0].fir = st[1].fir-1;
        for(int i = 1;i <= cnt;i++) {  //离散化
            if(st[i].fir - st[i-1].fir == 1) st1[i].fir = ++cc;
            else if(st[i].fir - st[i-1].fir > 1) {
                cc += 2;
                st1[i].fir = cc;
            }
            else st1[i].fir = cc;
            st1[i].se = st[i].se;
        }
        sort(st1+1,st1+cnt+1,cmp);
        for(int i = 1;i <= cnt;i += 2) {
            //printf("%d %d - %d %d\n",st1[i].fir,st1[i].se,st1[i+1].fir,st1[i+1].se);
            update(st1[i].fir,st1[i+1].fir,i,1,4*n,1);
            //for(int i = 1;i <= 4*n;i++) printf("%d-",query(i,1,4*n,1));
            //cout<<endl;
        }
        cnt = 0;
        for(int i = 1;i <= 4*n;i++) {
            dd[++cnt] = query(i,1,4*n,1);
        }
        sort(dd+1,dd+1+cnt);
        cc = 0;
        dd[0] = -1;
        for(int i = 1;i <= 4*n;i++) 
            if(dd[i] != dd[i-1]) cc++;
        printf("%d\n",cc-1);
    } 
}

/*
5
6
2 3
5 6
11 66
22 33
22 33
22 33
*/

F - Count the Colors

题意:
和前面一道题目一样,也是给你一个段,有n个操作,给你l,r,c,在区间l,r之间涂颜色c,问你n次之后有多少个可见颜色段。

题解:
和那道题作法一样,都是区间更新,最后单点询问。这里不区间查询是因为颜色的种类不能区间合并,当然也有区间查询的作法,我用的是单点询问。主要是这道题的线段定义方式不一样。
如图

不过这道题目的区间范围不大,所以我们直接把坐标*2,就可以解决问题。
代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 1e6+5;
LL mod = 1e9+7;
double eps = 0.00000001;

int n,col[maxn],f[maxn],dd[maxn],cnt,cc;

void init() {
    clr(col,0);
    clr(f,0);
}

void push_down(int root) {  //延迟更新
    col[root<<1] = col[root];
    col[root<<1|1] = col[root];
    f[root<<1] = f[root];
    f[root<<1|1] = f[root];
    f[root] = 0;
}

void update(int L,int R,int c,int l,int r,int root) { //成段更新
    if(L <= l && r <= R) {
        col[root] = c;
        f[root] = c;
        return ;
    }
    int m = (l+r)>>1;
    if(f[root]) push_down(root);
    if(m >= L) update(L,R,c,l,m,root<<1);
    if(m < R) update(L,R,c,m+1,r,root<<1|1);
}

int query(int pos,int l,int r,int root) {  //单点查询
    if(l == r && l == pos) {
        return col[root];
    }
    if(f[root]) push_down(root);
    int m = (l+r)>>1;
    if(m >= pos) query(pos,l,m,root<<1);
    else query(pos,m+1,r,root<<1|1);
}

int main() {
    while(scanf("%d",&n) != EOF) {
        init();
        int mm = 16005;
        for(int i = 1;i <= n;i++) {
            int l,r,c;
            scanf("%d%d%d",&l,&r,&c);
            update((l+1)*2,(r+1)*2,c+1,1,mm,1);
            //for(int i = 1;i <= 10;i++) printf("%d-",query(i,1,10,1));
            //cout<<endl;
        }
        map<int,int> mp;
        int pre = query(1,1,mm,1);
        mp[pre]++;
        for(int i = 1;i <= mm;i++) {
            int tmp = query(i,1,mm,1);
            //printf("%d-",tmp);
            if(tmp != pre) mp[tmp]++;
            pre = tmp;
        }
        map<int,int>::iterator ii;
        for(ii = mp.begin();ii != mp.end();ii++) {
            pii it = (*ii);
            if(!it.fir) continue;
            printf("%d %d\n",it.fir-1,it.se);
        }
        printf("\n");
    } 
}

HDU - 3974 J - Assign the task

题意:
给你n个村庄和m次操作。一开始n个村庄从左到右一排连在一起
也就是:1-2-3-4-5-6-7
D代表把一个村庄摧毁,比如D 3之后变成了:
1-2 3 4-5-6-7
R表示把最近的一次摧毁的村庄恢复
Q x代表询问x村庄和几个村庄相连

题解:
用线段树维护两个数组,R[maxn]和R[maxn],
R[pos]代表这个村庄可以到达的最右边的那个村庄,
L[pos]代表这个村庄可以到达的最左边的那个村庄。
每次更新两个数组即可。

比如样例就是:
7 9
1 1 1 1 1 1 1 –7 7 7 7 7 7 7
D 3
1 1 4 4 4 4 4 –2 2 2 7 7 7 7
D 6
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
D 5
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
Q 4
1
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
Q 5
0
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
R
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
Q 4
2
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
R
1 1 4 4 4 4 4 –2 2 2 7 7 7 7
Q 4
4
1 1 4 4 4 4 4 –2 2 2 7 7 7 7

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn =1e5+5;
const int inf = 0x3f3f3f3f;
LL mod = 1e9+7;
double eps = 0.00000001;

int L[maxn<<2],R[maxn<<2],m1[maxn<<2],m2[maxn<<2],n,m;

void build(int l,int r,int root) {
    if(l == r) {
        L[root] = 1;
        R[root] = n;
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,root<<1);
    build(m+1,r,root<<1|1);
}

void push_down_l(int root) {
    L[root<<1] = m1[root];
    L[root<<1|1] = m1[root];
    m1[root<<1] = m1[root];
    m1[root<<1|1] = m1[root];
    m1[root] = 0;
}

void update_l(int left,int right,int num,int l,int r,int root) {
    if(left <= l && r <= right) {
        L[root] = num;
        m1[root] = num;
        return ;
    }
    if(m1[root]) push_down_l(root);
    int m = (l+r)>>1;
    if(m >= left) update_l(left,right,num,l,m,root<<1);
    if(m < right) update_l(left,right,num,m+1,r,root<<1|1);
}

int query_l(int pos,int l,int r,int root) {
    if(l == r && pos == l) {
        if(m1[root]) return m1[root];
        else return L[root];
    }
    if(m1[root]) push_down_l(root);
    int m = (l+r)>>1;
    if(m >= pos) query_l(pos,l,m,root<<1);
    else query_l(pos,m+1,r,root<<1|1);
}

void push_down_r(int root) {
    R[root<<1] = m2[root];
    R[root<<1|1] = m2[root];
    m2[root<<1] = m2[root];
    m2[root<<1|1] = m2[root];
    m2[root] = 0;
}

void update_r(int left,int right,int num,int l,int r,int root) {
    if(left <= l && r <= right) {
        R[root] = num;
        m2[root] = num;
        return ;
    }
    if(m2[root]) push_down_r(root);
    int m = (l+r)>>1;
    if(m >= left) update_r(left,right,num,l,m,root<<1);
    if(m < right) update_r(left,right,num,m+1,r,root<<1|1);
}

int query_r(int pos,int l,int r,int root) {
    if(l == r && pos == l) {
        if(m2[root]) return m2[root];
        else return R[root];
    }
    if(m2[root]) push_down_r(root);
    int m = (l+r)>>1;
    if(m >= pos) query_r(pos,l,m,root<<1);
    else query_r(pos,m+1,r,root<<1|1);
}

int main() {
    while(scanf("%d%d",&n,&m) != EOF) {
    build(1,n,1);
    stack<int> q;
    clr(m1,0);
    clr(m2,0);
    /*for(int i = 1;i <= n;i++) printf("%d ",query_l(i,1,n,1));
    printf("--");
    for(int i = 1;i <= n;i++) printf("%d ",query_r(i,1,n,1));
    cout<<endl;*/
    for(int i = 1;i <= m;i++) {
        char str[2];int pos;
        scanf("%s",str);
        if(str[0] == 'D') {
            scanf("%d",&pos);
            int ll = query_l(pos,1,n,1),rr = query_r(pos,1,n,1);
            if(pos <= rr) update_l(pos,rr,pos+1,1,n,1);
            if(pos >= ll) update_r(ll,pos,pos-1,1,n,1);
            q.push(pos);
        }
        else if(str[0] == 'R') {
            if(q.empty()) continue;
            pos = q.top();q.pop();
            int ll,rr;
            if(pos > 1) ll = query_l(pos-1,1,n,1);
            else ll = 1;
            if(pos < n) rr = query_r(pos+1,1,n,1);
            else rr = n;
            if(pos <= rr) update_l(pos,rr,ll,1,n,1);
            if(pos >= ll) update_r(ll,pos,rr,1,n,1);
        }
        else {
            scanf("%d",&pos);
            int ll = query_l(pos,1,n,1),rr = query_r(pos,1,n,1);
            int ans = rr-ll+1;
            if(ans <= 0) ans = 0;
            printf("%d\n",ans);
        }
        /*for(int i = 1;i <= n;i++) printf("%d ",query_l(i,1,n,1));
        printf("--");
        for(int i = 1;i <= n;i++) printf("%d ",query_r(i,1,n,1));
        cout<<endl;*/
    }
    }
}

HDU - 4578 K - Transformation

题意:
很裸的线段树,题意也很明显。不再赘述
题解:
这道题目一共有四种操作和三种询问,加在一起就很麻烦。
首先要知道push_down操作的顺序。
先更新op = 3,也就是替换操作。因为替换操作后之前的所有操作都不算了,也就是可以直接往下推,把op = 1和op = 2的标记清空。
再更新op = 2,也就是乘操作。因为更新完替换操作之后,乘操作也可以直接往下推,同时更新下面的加标记和乘标记。
最后更新op = 1,也就是加操作。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn =1e5+5;
const int inf = 0x3f3f3f3f;
LL mod = 10007;
double eps = 0.00000001;

struct node {
    LL sum1,sum2,sum3;
    int mark1,mark2,mark3;
} rt[maxn<<2];

void push_up(int root) {
    rt[root].sum1 = (rt[root<<1].sum1+rt[root<<1|1].sum1)%mod;
    rt[root].sum2 = (rt[root<<1].sum2+rt[root<<1|1].sum2)%mod;
    rt[root].sum3 = (rt[root<<1].sum3+rt[root<<1|1].sum3)%mod;
}

void push_down(int root,int cl,int cr) {
    if(rt[root].mark1) {   //这里先更新替换操作,因为替换操作同时也可以直接更新乘和加操作
        LL c = rt[root].mark1;
        rt[root<<1].mark1 = rt[root<<1|1].mark1 = c;
        rt[root<<1].mark3 = rt[root<<1|1].mark3 = 0;
        rt[root<<1].mark2 = rt[root<<1|1].mark2 = 1;
        rt[root].mark1 = 0;
        rt[root<<1].sum1 = cl * c % mod;
        rt[root<<1].sum2 = cl * c * c % mod;
        rt[root<<1].sum3 = cl * c * c * c % mod;
        rt[root<<1|1].sum1 = cr * c % mod;
        rt[root<<1|1].sum2 = cr * c * c % mod;
        rt[root<<1|1].sum3 = cr * c * c * c % mod;
    }
    if(rt[root].mark2 != 1) {
        LL c = rt[root].mark2;
        rt[root<<1].mark2 = rt[root<<1].mark2 * c % mod;    //更新子树的乘和加的标记
        rt[root<<1].mark3 = rt[root<<1].mark3 * c % mod;
        rt[root<<1|1].mark2 = rt[root<<1|1].mark2 * c % mod;
        rt[root<<1|1].mark3 = rt[root<<1|1].mark3 * c % mod;
        rt[root].mark2 = 1;
        rt[root<<1].sum1 = rt[root<<1].sum1 * c % mod;  //更新子树的乘和加的和
        rt[root<<1].sum2 = rt[root<<1].sum2 * c * c % mod;
        rt[root<<1].sum3 = rt[root<<1].sum3 * c * c * c % mod;
        rt[root<<1|1].sum1 = rt[root<<1|1].sum1 * c % mod;
        rt[root<<1|1].sum2 = rt[root<<1|1].sum2 * c * c % mod;
        rt[root<<1|1].sum3 = rt[root<<1|1].sum3 * c * c * c % mod;
    }
    if(rt[root].mark3) {
        LL c = rt[root].mark3;
        rt[root<<1].mark3 = (rt[root<<1].mark3 + c) % mod;
        rt[root<<1|1].mark3 = (rt[root<<1|1].mark3 + c) % mod;
        rt[root].mark3 = 0;
        LL lx1 = rt[root<<1].sum1,lx2 = rt[root<<1].sum2,lx3 = rt[root<<1].sum3;
        LL rx1 = rt[root<<1|1].sum1,rx2 = rt[root<<1|1].sum2,rx3 = rt[root<<1|1].sum3;
        rt[root<<1].sum1 = (lx1 + cl*c) % mod;  //更新子树的加的和
        rt[root<<1].sum2 = (lx2 + 2*c*lx1 + cl*c*c) % mod;
        rt[root<<1].sum3 = (lx3 + 3*lx2*c + 3*lx1*c*c + cl*c*c*c) % mod;
        rt[root<<1|1].sum1 = (rx1 + cr*c) % mod;    
        rt[root<<1|1].sum2 = (rx2 + 2*c*rx1 + cr*c*c) % mod;
        rt[root<<1|1].sum3 = (rx3 + 3*rx2*c + 3*rx1*c*c + cr*c*c*c) % mod;
    }
}   

void build(int l,int r,int root) {
    if(l == r) {
        rt[root].sum1 = rt[root].sum2 = rt[root].sum3 = 0;
        rt[root].mark1 = rt[root].mark3 = 0;
        rt[root].mark2 = 1;
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,root<<1);
    build(m+1,r,root<<1|1);
    rt[root].sum1 = rt[root].sum2 = rt[root].sum3 = 0;
    rt[root].mark1 = rt[root].mark3 = 0;
    rt[root].mark2 = 1;
}

void update_chg(int L,int R,int c,int l,int r,int root) {  //更新替换操作
    if(L <= l && r <= R) { 
        LL dis = r-l+1;
        rt[root].sum1 = dis * c % mod;
        rt[root].sum2 = dis * c * c % mod;
        rt[root].sum3 = dis * c * c * c % mod;
        rt[root].mark1 = c;   //因为替换了,其他的标记就可以直接清空
        rt[root].mark2 = 1;
        rt[root].mark3 = 0;
        //printf("%d - %d --- %d\n",l,r,rt[root].sum1);
        return ;
    }
    int m = (l+r)>>1;
    push_down(root,m-l+1,r-m);
    if(m >= L) update_chg(L,R,c,l,m,root<<1);
    if(m < R) update_chg(L,R,c,m+1,r,root<<1|1);
    push_up(root);
}

void update_mul(int L,int R,int c,int l,int r,int root) {  //更新乘操作
    if(L <= l && r <= R) {
        LL x1 = rt[root].sum1,x2 = rt[root].sum2,x3 = rt[root].sum3;
        rt[root].sum3 = (x3 * c * c * c) % mod;
        rt[root].sum2 = (x2 * c * c) % mod;
        rt[root].sum1 = (x1 * c) % mod;
        rt[root].mark2 = rt[root].mark2 * c % mod;  //如果之前已经有加操作了,就把它直接乘c
        rt[root].mark3 = rt[root].mark3 * c % mod;
        return ;
    }
    int m = (l+r)>>1;
    push_down(root,m-l+1,r-m);
    if(m >= L) update_mul(L,R,c,l,m,root<<1);
    if(m < R) update_mul(L,R,c,m+1,r,root<<1|1);  
    push_up(root);
}

void update_add(int L,int R,int c,int l,int r,int root) {  //更新加操作
    if(L <= l && r <= R) {
        LL x1 = rt[root].sum1,x2 = rt[root].sum2,x3 = rt[root].sum3,dis = r-l+1;
        rt[root].sum3 = (x3 + 3*x2*c + 3*x1*c*c + dis*c*c*c) % mod;
        rt[root].sum2 = (x2 + 2*c*x1 + dis*c*c) % mod;
        rt[root].sum1 = (x1 + dis*c) % mod;
        rt[root].mark3 = (rt[root].mark3 + c) % mod;
        return ;
    }
    int m = (l+r)>>1;
    push_down(root,m-l+1,r-m);
    if(m >= L) update_add(L,R,c,l,m,root<<1);
    if(m < R) update_add(L,R,c,m+1,r,root<<1|1);
    push_up(root);
}

LL query(int L,int R,int c,int l,int r,int root) {
    if(L <= l && r <= R) {
        if(c == 1) return rt[root].sum1;
        if(c == 2) return rt[root].sum2;
        if(c == 3) return rt[root].sum3;
    }
    int m = (l+r)>>1;
    push_down(root,m-l+1,r-m);
    LL ans = 0;
    if(m >= L) ans += query(L,R,c,l,m,root<<1);
    if(m < R) ans += query(L,R,c,m+1,r,root<<1|1);
    return (ans%mod);
}

main() {
    //freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    int n,m;
    while(scanf("%d%d",&n,&m) != EOF && (n || m)) {
        build(1,n,1);
        for(int i = 1;i <= m;i++) {
            int op,l,r,c;
            scanf("%d%d%d%d",&op,&l,&r,&c);
            if(op == 1) update_add(l,r,c,1,n,1);
            if(op == 2) update_mul(l,r,c,1,n,1);
            if(op == 3) update_chg(l,r,c,1,n,1);
            if(op == 4) printf("%I64d\n",query(l,r,c,1,n,1)%mod);
            //for(int i = 1;i <= n;i++) printf("%I64d ",query(i,i,1,1,n,1));
            //cout<<endl;
        }
    }
}

/*
5 4
3 1 3 5
3 1 2 8
3 4 5 3
3 2 2 7
*/

HDU - 4614 L - Vases and Flowers (二分+线段树)

题意:
n个花瓶,m次操作。
op = 1,从pos = k开始一直到pos = n,给你x朵花,碰到空花瓶就插花进去,直到花插完或者到了pos = n,如果多的花就丢了。
op = 2,给你l,r,问你当前l ~ r之间的花瓶内有多少朵花,并且把l ~ r之间的花瓶全部清空

题解:
首先,op = 2的时候,直接查询l ~ r之间的sum就可以,然后再update区间l ~ r的花为0
之后是op = 1。因为每个花瓶只能放一朵花,那么我们查询某个区间的sum,再减去区间内的花瓶数,就可以知道这个区间可以放多少朵花。知道这个之后,我们先求出从k ~ n我们可以查多少朵花,也就是nn = min(x , n-k+1 - query(k,n,1,n,1))
,然后在区间k ~ n二分求两个点,刚好可以插一朵花和可以插nn朵花。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn =1e5+5;
const int inf = 0x3f3f3f3f;
LL mod = 10007;
double eps = 0.00000001;

int n,m,sum[maxn<<2],mark[maxn<<2];

void push_up(int root) {
    sum[root] = sum[root<<1]+sum[root<<1|1];
}

void push_down(int root,int cl,int cr) {
    mark[root<<1] = mark[root];
    mark[root<<1|1] = mark[root];
    sum[root<<1] = cl*mark[root];
    sum[root<<1|1] = cr*mark[root];
    mark[root] = -1;
}

void build(int l,int r,int root) {
    if(l == r) {
        sum[root] = 0;
        mark[root] = -1;
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,root<<1);
    build(m+1,r,root<<1|1);
    push_up(root);
    mark[root] = -1;
}

void update(int L,int R,int c,int l,int r,int root) {
    if(L <= l && r <= R) {
        sum[root] = (r-l+1)*c;
        mark[root] = c;
        return ;
    }
    int m = (l+r)>>1;
    if(mark[root] != -1) push_down(root,m-l+1,r-m);
    if(m >= L) update(L,R,c,l,m,root<<1);
    if(m < R) update(L,R,c,m+1,r,root<<1|1);
    push_up(root);
}

int query(int L,int R,int l,int r,int root) {
    if(L <= l && r <= R) {
        return sum[root];
    }
    int m = (l+r)>>1;
    if(mark[root] != -1) push_down(root,m-l+1,r-m);
    int ans = 0;
    if(m >= L) ans += query(L,R,l,m,root<<1);
    if(m < R) ans += query(L,R,m+1,r,root<<1|1);
    push_up(root);
    return ans;
}

int get_pos(int s,int x) {
    int l = s,r = n;
    if(x == 1 && query(s,s,1,n,1) == 0) return s;
    while(l + 1 < r) {
        int m = (l+r)>>1;
        if(m-s+1 - query(s,m,1,n,1) >= x) r = m;
        else l = m;
        //printf("%d-%d--%d\n",l,r,x);
    }
    return r;
}

int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&m);
        build(1,n,1);
        for(int i = 1;i <= m;i++) {
            int op,l,r;
            scanf("%d%d%d",&op,&l,&r);
            l++,r;
            if(op == 1) {
                int cnt = query(l,n,1,n,1);
                //printf("%d++++",cnt);
                if(cnt == n-l+1) {
                    puts("Can not put any one.");
                    continue;
                }
                int nn = min(r,n-l+1-cnt);
                int ll = get_pos(l,1),rr = get_pos(l,nn);
                update(ll,rr,1,1,n,1);
                printf("%d %d\n",ll-1,rr-1);
            }
            else {
                r++;
                printf("%d\n",query(l,r,1,n,1));
                update(l,r,0,1,n,1);
            }
            //for(int i = 1;i <= n;i++) printf("%d ",query(i,i,1,n,1));
            //cout<<endl;
        }
        cout<<endl;
    }
}

HDU - 4553 M - 约会安排(区间合并)

题意:
给你长度为T的时间,m次询问
每次询问有三种情况:
屌丝,分配最近的长度为x还没有占用一块连续区间给他
女神,也是分配最近的长度为x还没有占用一块连续区间给他,但是如果找不到区间,就再去无视屌丝的占用再查询一次。
清空L-R之间所有的时间占用。

题解:
用到线段树的区间合并思想,维护每个区间对于屌丝的还没有占用的长度,dl,dr,dm。dl是该区间左边连续的空区间,dr是该区间右边连续的空区间,dm是该区间最长的连续的空区间。
对于女神就是nl,nr和nm。再用一个mark当懒惰标记。因为每一种更新都可以直接覆盖之前的更新,所以就只要一个标记,用不同的数字表示即可。1表示屌丝,2
表示女神,3表示清空。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn =1e5+5;
const int inf = 0x3f3f3f3f;
LL mod = 10007;
double eps = 0.00000001;

int n,m;
struct node {
    int dl,dr,dm,nl,nr,nm,mark;
} rt[maxn<<2];

void build(int l,int r,int root) {  //建树,主要是初始化
    if(l == r) {
        rt[root].dl = rt[root].dr = rt[root].dm = 1;
        rt[root].nl = rt[root].nr = rt[root].nm = 1;
        rt[root].mark = 0;
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,root<<1);
    build(m+1,r,root<<1|1);
    rt[root].dl = rt[root].dr = rt[root].dm = r-l+1;
    rt[root].nl = rt[root].nr = rt[root].nm = r-l+1;
    rt[root].mark = 0;
}

void push_up(int root,int cl,int cr) {  //区间合并
    if(rt[root<<1].dl == cl) rt[root].dl = rt[root<<1].dl + rt[root<<1|1].dl;  
    else rt[root].dl = rt[root<<1].dl;
    if(rt[root<<1|1].dr == cr) rt[root].dr = rt[root<<1].dr + rt[root<<1|1].dr;
    else rt[root].dr = rt[root<<1|1].dr;
    rt[root].dm = max((rt[root<<1].dr+rt[root<<1|1].dl) , max(rt[root<<1].dm,rt[root<<1|1].dm));

    if(rt[root<<1].nl == cl) rt[root].nl = rt[root<<1].nl + rt[root<<1|1].nl;
    else rt[root].nl = rt[root<<1].nl;
    if(rt[root<<1|1].nr == cr) rt[root].nr = rt[root<<1].nr + rt[root<<1|1].nr;
    else rt[root].nr = rt[root<<1|1].nr;
    rt[root].nm = max((rt[root<<1].nr+rt[root<<1|1].nl) , max(rt[root<<1].nm,rt[root<<1|1].nm));
}

void push_down(int root,int cl,int cr) {  //根据不同标记,下推结果
    if(rt[root].mark == 1) {
        rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = 0;
        rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = cl;
        rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = 0;
        rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = cr;
    }
    if(rt[root].mark == 2) {
        rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = 0;
        rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = 0;
        rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = 0;
        rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = 0;
    }
    if(rt[root].mark == 3) {
        rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = cl;
        rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = cl;
        rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = cr;
        rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = cr;
    }
    rt[root<<1].mark = rt[root<<1|1].mark = rt[root].mark;
    rt[root].mark = 0;
}

void update(int L,int R,int op,int l,int r,int root) {
    if(L <= l && r <= R) {
        if(op == 1) {
            rt[root].dl = rt[root].dr = rt[root].dm = 0;
            rt[root].nl = rt[root].nr = rt[root].nm = r-l+1;
        }
        if(op == 2) {
            rt[root].dl = rt[root].dr = rt[root].dm = 0;
            rt[root].nl = rt[root].nr = rt[root].nm = 0;
        }
        if(op == 3) {
            rt[root].dl = rt[root].dr = rt[root].dm = r-l+1;
            rt[root].nl = rt[root].nr = rt[root].nm = r-l+1;
        }
        rt[root].mark = op;
        return ;
    }
    int m = (l+r)>>1;
    if(rt[root].mark) push_down(root,m-l+1,r-m);    
    if(m >= L) update(L,R,op,l,m,root<<1);
    if(m < R) update(L,R,op,m+1,r,root<<1|1);
    push_up(root,m-l+1,r-m);
}

int query(int len,int op,int l,int r,int root) {
    //printf("%d--%d\n",l,r);
    if(l == r) return l;
    int m = (l+r)>>1;
    if(rt[root].mark) push_down(root,m-l+1,r-m);    
    if(op == 1) {
        //printf("%d ++++ %d\n",rt[root<<1].dm,len);
        if(rt[root<<1].dm >= len) return query(len,op,l,m,root<<1);
        else if(rt[root<<1].dr + rt[root<<1|1].dl >= len) return m-rt[root<<1].dr+1;
        else return query(len,op,m+1,r,root<<1|1);
    }
    else if(op == 2) {
        if(rt[root<<1].nm >= len) return query(len,op,l,m,root<<1);
        else if(rt[root<<1].nr + rt[root<<1|1].nl >= len) return m-rt[root<<1].nr+1;
        else return query(len,op,m+1,r,root<<1|1);
    }
}

int main() {
    int t,tt = 0;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&m);
        build(1,n,1);
        printf("Case %d:\n",++tt);
        for(int i = 1;i <= m;i++) {
            char str[15];
            int x,l,r;
            scanf("%s",str);
            if(str[0] == 'D') {
                scanf("%d",&x);
                if(rt[1].dm >= x) {
                    int pos = query(x,1,1,n,1);
                    update(pos,pos+x-1,1,1,n,1);
                    printf("%d,let's fly\n",pos);
                }
                else puts("fly with yourself");
            }
            else if(str[0] == 'N') {
                scanf("%d",&x);
                if(rt[1].dm >= x) {
                    int pos = query(x,1,1,n,1);
                    printf("%d,don't put my gezi\n",pos);
                    update(pos,pos+x-1,2,1,n,1);
                }
                else if(rt[1].nm >= x) {
                    int pos = query(x,2,1,n,1);
                    printf("%d,don't put my gezi\n",pos);
                    update(pos,pos+x-1,2,1,n,1);
                }
                else puts("wait for me");
            }
            else {
                scanf("%d%d",&l,&r);
                update(l,r,3,1,n,1);
                puts("I am the hope of chinese chengxuyuan!!");
            }
        }
    }
}

HDU - 1542 P - Atlantis (扫描线)

题意:
一共有n个矩形,接下来n行,分别是第 i 个矩形的左下角和右上角坐标,问你这些矩形叠在一起的总面积是多少。

题解:
线段树扫描线模板题。
扫描线的思想是把所有的线都转化成横线,并且按高度排好序。每次把当前的高度所有有效的横线*高度就是面积。当前高度的有效横线的长度就用线段树维护,而且先要用map什么的离散化一下。不是很难的算法。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 205;
const int inf = 0x3f3f3f3f;
LL mod = 10007;
double eps = 0.00000001;

int n,mark[maxn<<2];
double sum[maxn<<2],has[maxn];
map<double,int> mp;
struct node {
    double l,r,h;  //横线的左边,右边和高度
    int d;  //d为1代表矩形底边,-1代表顶边
    node(){}
    node(double x1,double x2,double h1,int dd):l(x1),r(x2),h(h1),d(dd){}
} s[maxn];

bool cmp(node a,node b) {
    return a.h < b.h;
}

void push_up(int l,int r,int root) {
    if(mark[root]) sum[root] = has[r+1] - has[l];
    else if(l == r) sum[root] = 0;
    else sum[root] = sum[root<<1] + sum[root<<1|1];
}

void update(int L,int R,int d,int l,int r,int root) {
    if(L <= l && r <= R) {
        mark[root] += d;
        push_up(l,r,root);
        return ;
    }
    int m = (l+r)>>1;
    if(m >= L) update(L,R,d,l,m,root<<1);
    if(m < R) update(L,R,d,m+1,r,root<<1|1);
    push_up(l,r,root);
}

int main() {
    int tt = 0;
    while(scanf("%d",&n) != EOF && n) {
        mp.clear();
        for(int i = 1;i <= n;i++) {
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            s[i*2-1] = node(x1,x2,y1,1);   
            s[i*2] = node(x1,x2,y2,-1);  //把点都化成横线的形式
            mp[x1] = 1;
            mp[x2] = 1;   //离散化横坐标
        }
        int cnt = 0;
        has[0] = 0;
        for(auto it : mp) {  //离散化并存到has数组里
            double tmp = it.fir;
            mp[tmp] = ++cnt;
            has[cnt] = tmp;
        }
        sort(s+1,s+1+n*2,cmp);  //按高度对横线排序
        double ans = 0; 
        clr(sum,0);
        clr(mark,0);
        for(int i = 1;i <= n*2;i++) {
            update(mp[s[i].l],mp[s[i].r]-1,s[i].d,1,cnt,1);  //用线段树维护横线的长度。
            ans += sum[1]*(s[i+1].h - s[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",++tt,ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值