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

来源:互联网 发布:网络鲜花店 上海 编辑:程序博客网 时间:2024/06/10 21:46

温故知新,现在代码水平好了一些,重新刷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 longtypedef 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 longtypedef 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);    } }/*562 35 611 6622 3322 3322 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 longtypedef 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 longtypedef 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 longtypedef 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 43 1 3 53 1 2 83 4 5 33 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 longtypedef 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 longtypedef 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 longtypedef 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);    }}