kuangbin带我飞 专题七 线段树专题(一波线段树水题,难题还不会做)

第一次学习线段树,树状数组RMQ等,努力学习,加油。

HDU 1166

题意:很清楚吧

题解:用树状数组求连续和,非常快。

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       50000+5
#define   MAXN      100000+5
#define   lson      2*i
#define   rson      2*i+1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int c[MAX];
int a[MAX];

int sum(int x){
    int ans=0;
    while(x>0){
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}

void add(int x,int d){
    while(x<=MAX){
        c[x]+=d;
        x+=lowbit(x);
    }
}
int main(){
    int T;
    scanf("%d",&T);
    for(int t=1;t<=T;t++){
        int n;
        scanf("%d",&n);
        mem0(c);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            add(i,a[i]);
        }
        string s;
        printf("Case %d:\n",t);
        while(cin>>s){
            int a,b;
            if(s[0]=='Q'){
                scanf("%d%d",&a,&b);
                printf("%d\n",sum(b)-sum(a-1));
            }
            else if(s[0]=='A'){
                scanf("%d%d",&a,&b);
                add(a,b);
            }
            else if(s[0]=='S'){
                scanf("%d%d",&a,&b);
                add(a,-b);
            }
            else if(s[0]=='E') break;
        }
    }
    return 0;
}

HDU 1754

题意:都明白

题解:很明显是线段树,然而我第一次撸线段树,TLE RE各种死,首先这题有点阴,给20W点,肯定会RE,看discuss里说要开三倍大才能过

然后就是建树,更新,查询,我就是更新的地方写烦了然后T了好久,更新的点应该类似于二分找到那个位置。。(单点更新)

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       200000+5
#define   MAXN      100000+5
#define   lson      2*i
#define   rson      2*i+1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int maxv[MAX*12];
int a[MAX*3];
int l,r;
int n,m;

void build(int i,int L,int R){
    int mid=L+(R-L)/2;
    if(L==R) maxv[i]=a[L];
    else{
        build(lson,L,mid);
        build(rson,mid+1,R);
        maxv[i]=max(maxv[lson],maxv[rson]);
    }
}

void update(int i,int L,int R,int l,int r){
    int mid=L+(R-L)/2;
    if(L==R){
        maxv[i]=r;
        return;
    }
    if(l<=mid) update(lson,L,mid,l,r);
    else update(rson,mid+1,R,l,r);
    maxv[i]=max(maxv[lson],maxv[rson]);
}

int query(int i,int L,int R){
    int mid=L+(R-L)/2;
    int ans=0;
    int a=0,b=0;
    if(l<=L&&R<=r) return maxv[i];
    if(l<=mid) a=query(lson,L,mid);
    if(r>mid) b=query(rson,mid+1,R);
    return ans=max(a,b);
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        mem0(maxv); mem0(a);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        build(1,1,n);
        while(m--){
            char c;
            getchar();
            scanf("%c%d%d",&c,&l,&r);
            if(c=='Q') printf("%d\n",query(1,1,n));
            else{
                update(1,1,n,l,r);
            }
        }
    }
    return 0;
}

poj  3468

题解:就是线段树区间修改值,看着是个水题,模板题,然而我T啊WA 啊RE啊大半天了,不行了搞得我困死了

学习了一发完全版线段树的模板,慢慢体会了,必须靠题量和时间慢慢理解了,谁叫上学期不认真没有早点学线段树呢(区间更新)

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       1000000+5
#define   MAXN      100000+5
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

LL sum[MAX<<2];
LL add[MAX<<2];
void PushUp(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void PushDown(int rt,int m){
    if(add[rt]){
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=add[rt]*(m-(m>>1));
        sum[rt<<1|1]+=add[rt]*(m>>1);
        add[rt]=0;
    }
}

void build(int l,int r,int rt){
    if(l==r){
        scanf("%lld",&sum[rt]);
        return ;
    }
    int m=l+(r-l)/2;
    build(lson);
    build(rson);
    PushUp(rt);
}

void update(int L,int R,LL c,int l,int r,int rt){
    if(L<=l&&r<=R){
        add[rt]+=c;
        sum[rt]+=c*(r-l+1);
    }
    else{
        PushDown(rt,r-l+1);
        int m=l+(r-l)/2;
        if(L<=m) update(L,R,c,lson);
        if(R>m) update(L,R,c,rson);
        PushUp(rt);
    }
}

LL query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R) return sum[rt];
    PushDown(rt,r-l+1);
    int m=l+(r-l)/2;
    LL ans=0;
    if(L<=m) ans+=query(L,R,lson);
    if(R>m) ans+=query(L,R,rson);
    return ans;
}
int main(){
    int n,q;
    mem0(add);
    scanf("%d%d",&n,&q);
    build(1,n,1);
    while(q--){
        char ch;
        int a,b;
        LL c;
        getchar();
        scanf("%c",&ch);
        if(ch=='Q'){
            scanf("%d%d",&a,&b);
            printf("%lld\n",query(a,b,1,n,1));
        }
        else{
            scanf("%d%d%lld",&a,&b,&c);
            update(a,b,c,1,n,1);
        }
    }
    return 0;
}

poj 2528

题意:给你n个海报,按照顺序贴墙上,问你最后能看到多少个(未被覆盖的有多少个)

题解:区间的维护,用线段树(虽然本渣一开始完全想不到线段树怎么弄啊)

这题数据有点大,所以需要离散化(排序去重

但是只是这样简单的离散化是错误的,

如三张海报为:1~10 1~4 6~10

离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一张海报时:墙的1~4被染为1;
第二张海报时:墙的1~2被染为2,3~4仍为1;
第三张海报时:墙的3~4被染为3,1~2仍为2。
最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。

新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)

X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10

这样之后,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3

最终,1~2为2,3为1,4~5为3,于是输出正确结果3。

(然而poj这么写了会WA,不这么离散反而能AC,不造为啥,反正知道这个方法是对的就行了)

线段树还得多刷题

<span style="font-size:12px;">#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       20000+5
#define   MAXN      100000+5
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int l[MAX],r[MAX];
int x[MAX<<3];
int root[MAX<<4];
int vis[MAX<<2];
int cnt;

void PushDown(int rt){
    root[rt<<1]=root[rt<<1|1]=root[rt];
    root[rt]=-1;
}

void update(int l,int r,int rt,int c,int L,int R){
    if(L<=l&&r<=R){
        root[rt]=c;
        return;
    }
    if(root[rt]!=-1) PushDown(rt);
    int m=l+(r-l)/2;
    if(L<=m) update(lson,c,L,R);
    if(R>m) update(rson,c,L,R);
}

int Bsearch(int a,int b,int xx){
    int l=a,r=b;
    while(l<=r){
        int mid=(l+r)/2;
        if(x[mid]<xx) l=mid+1;
        else r=mid-1;
    }
    return l;
}

void query(int l,int r,int rt){
    if(l==r){
        if(!vis[root[rt]]){
            cnt++;
            vis[root[rt]]=1;
        }
        return;
    }
    if(root[rt]!=-1) PushDown(rt);
    int m=l+(r-l)/2;
    query(lson);
    query(rson);
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        int nn=0;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&l[i],&r[i]);
            x[++nn]=l[i];
            x[++nn]=r[i];
        }
        sort(x+1,x+nn+1);
        int mm=1;
        for(int i=2;i<=nn;i++){
            if(x[i]!=x[i-1]) x[++mm]=x[i];
        }
        mem1(root);
        mem0(vis);
        for(int i=1;i<=n;i++){
            int L=Bsearch(1,mm,l[i]);
            int R=Bsearch(1,mm,r[i]);
            update(1,mm,1,i,L,R);
        }
        cnt=0;
        query(1,mm,1);
        printf("%d\n",cnt);
    }
    return 0;
}
</span>

hdu 1698

题意:屠夫的钩子有n段,每段的值为1,现在屠夫可以在一段区间内把钩子的值变成1,2,3;

题解:显然是线段树,区间替换,这题只需要更新,然后询问rt=1的点,所以没有写query函数,感觉还是比较清楚的(第一次写区间更换,感觉还得多刷点模板题,模板都没写熟练呢,哎)还有数组每次开4倍,*4总是RE,写成<<2就能过了,什么仇什么怨

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       100000+5
#define   MAXN      100000+5
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int sum[MAX<<2];
int col[MAX<<2];

void pushup(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void pushdown(int rt,int m){
    if(col[rt]){
        col[rt<<1]=col[rt<<1|1]=col[rt];
        sum[rt<<1]=col[rt]*(m-(m>>1));
        sum[rt<<1|1]=col[rt]*(m>>1);
        col[rt]=0;
    }
}

void build(int l,int r,int rt){
    if(l==r){
        sum[rt]=1;
        return;
    }
    int m=l+(r-l)/2;
    build(lson);
    build(rson);
    pushup(rt);
}

void update(int l,int r,int rt,int L,int R,int c){
    if(L<=l&&r<=R){
        col[rt]=c;
        sum[rt]=c*(r-l+1);
        return;
    }
    pushdown(rt,r-l+1);
    int m=l+(r-l)/2;
    if(L<=m) update(lson,L,R,c);
    if(R>m) update(rson,L,R,c);
    pushup(rt);
}


int main(){
    int t;
    scanf("%d",&t);
    int kase=0;
    while(t--){
        int n,q;
        scanf("%d%d",&n,&q);
        mem0(col);
        build(1,n,1);
        while(q--){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            update(1,n,1,a,b,c);
        }
        printf("Case %d: The total value of the hook is %d.\n",++kase,sum[1]);
    }
    return 0;
}

zoj 1610

题意:给你一段区间,从0-8000(这是个坑点,一开始以为是0-n,跪了会),然后给你n次涂色,每次可以覆盖以前的,问你最后墙上每种颜色出现了多少段(如果同种颜色中间被其他颜色或者没有颜色分开了,那么就是两段)

题解:线段树区间维护啊,然后给你的是点,让你涂色的两点之间的区间,所以把输入的左端点+1,把区间压缩成点,这样容易想一点,就是每次涂色的是一段点,然后求多少段。

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       8000+5
#define   MAXN      100000+5
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}


int col[MAX<<2];
int vis[MAX<<2];
int ans[MAX<<2];
int colour[MAX<<2];

void pushdown(int rt){
    if(col[rt]!=-1){
        col[rt<<1]=col[rt<<1|1]=col[rt];
        col[rt]=-1;
    }
}

void update(int l,int r,int rt,int L,int R,int c){
    if(L<=l&&r<=R){
        col[rt]=c;
        return;
    }
    pushdown(rt);
    int m=l+(r-l)/2;
    if(L<=m) update(lson,L,R,c);
    if(R>m) update(rson,L,R,c);
}

void query(int l,int r,int rt){
    if(l==r){
        if(col[rt]!=-1){
            ans[l]=col[rt];
        }
        return;
    }
    pushdown(rt);
    int m=l+(r-l)/2;
    query(lson);
    query(rson);
}
int main(){
    int n;
    while(~scanf("%d",&n)){
        mem1(col);
        mem0(vis);
        mem1(ans);
        mem0(colour);
        for(int i=0;i<n;i++) {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            update(1,8000,1,a+1,b,c);
        }
        query(1,8000,1);
        for(int i=1;i<=8000;i++){
            if(ans[i]!=-1){
                if(i==1) colour[ans[i]]++;
                else if(ans[i]!=ans[i-1]) colour[ans[i]]++;
            }
        }
        for(int i=0;i<=8000;i++){
            if(colour[i]) printf("%d %d\n",i,colour[i]);
        }
        printf("\n");
    }
    return 0;
}

poj 3264

题意:给你n个牛,求区间内最高的牛和最低的牛的差

题解:线段树维护区间内的最大最小值啊怒1A

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       50000+5
#define   MAXN      100000+5
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int a[MAX];
int maxv[MAX<<2];
int minv[MAX<<2];
int _max,_min;

void pushup(int rt){
    maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
    minv[rt]=min(minv[rt<<1],minv[rt<<1|1]);
}

void build(int l,int r,int rt){
    if(l==r){
        scanf("%d",&maxv[rt]);
        minv[rt]=maxv[rt];
        return;
    }
    int m=l+(r-l)/2;
    build(lson);
    build(rson);
    pushup(rt);
}

void query(int l,int r,int rt,int L,int R){
    if(L<=l&&r<=R){
        _max=max(_max,maxv[rt]);
        _min=min(_min,minv[rt]);
        return ;
    }
    int m=l+(r-l)/2;
    if(L<=m) query(lson,L,R);
    if(R>m) query(rson,L,R);
}
int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    build(1,n,1);
    while(q--){
        int a,b;
        scanf("%d%d",&a,&b);
        _max=0;
        _min=INF;
        query(1,n,1,a,b);
        printf("%d\n",_max-_min);
    }
    return 0;
}

hdu 4027

题意:给你n个数,然后q次询问,t=0的时候把区间内的值都开根取整,t=1的时候就输出区间的和

题解: 

刚开始的时候第一反应这道题是成段的更新,不能一个数一个数的更新,那样肯定会超时的!
但是再想了一会儿之后,发现成段更新比较困难,主要是这道题的每个节点的更新并不是统一的(也就是说并不是都加K,或者都减K之类的);
所以我们并不能像一般的成段更新那样去更新。
再仔细观察后,其实我们可以很容易的发现,一个数k(k<=2^63-1)在经过最多6,7次的开平方根后,必然会变成1,而且当1的平方根也是1;
也就是说当一个数为1的时候,我们没有必要对它进行操作和更新;而且一个很大的数仅仅经过6,7次就可以变成1;
所以到这里我们因该就可以形成一个解题的大体思路了:
每当我们要进行更新操作的时候,我们先判断一下这个区间是否有必要进行更新(若全都是1就没有更新的必要了);
判断的方法很简单:就是看该区间的长度和该区间内的总值是否相等;
然后要不停的更新
接下来的就是很水很水的区间求和了!!!!!!!!!!(给的区间X,Y有可能是X>Y,坑爹啊)


#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       100000+5
#define   MAXN      100000+5
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

LL sum[MAX<<2];
LL ans;

void pushup(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void build(int l,int r,int rt){
    if(l==r){
        scanf("%I64d",&sum[rt]);
        return;
    }
    int m=l+(r-l)/2;
    build(lson);
    build(rson);
    pushup(rt);
}

void update(int l,int r,int rt,int L,int R){
    if(sum[rt]==(LL)(r-l+1)) return;
    if(l==r){
        sum[rt]=sqrt(double(sum[rt]));
        return;
    }
    int m=l+(r-l)/2;
    if(L<=m) update(lson,L,R);
    if(R>m) update(rson,L,R);
    pushup(rt);
}

void query(int l,int r,int rt,int L, int R){
    if(L<=l&&r<=R){
        ans+=sum[rt];
        return;
    }
    int m=l+(r-l)/2;
    if(L<=m) query(lson,L,R);
    if(R>m) query(rson,L,R);
}
int main(){
    int n;
    int kase=0;
    while(~scanf("%d",&n)){
        kase++;
        printf("Case #%d:\n",kase);
        build(1,n,1);
        int q;
        scanf("%d",&q);
        while(q--){
            int t,a,b;
            scanf("%d%d%d",&t,&a,&b);
            if(a>b) swap(a,b);
            if(t==0){
                update(1,n,1,a,b);
            }
            else{
                ans=0;
                query(1,n,1,a,b);
                printf("%I64d\n",ans);
            }
        }
        printf("\n");
    }
    return 0;
}

hdu 1394

题意:给你n个数字,然后这个序列可循环(就是可以每次把头上一个数字放到序列的最后),且这个序列的数字不重复,从0-n-1

然后问你这样的n个序列中逆序对数最小是多少

题解:我觉得这题好难啊,为啥过的人那么多而且AC率这么高。可能我太弱了吧,刚接触线段树

然后这题的思路是先找到输入的序列的逆序对数,然后头上一个数字放到最后,如果头上的数字是a[i],那么放到后面,逆序对数量的变化是-a[i]+n-1-a[i],所以只需要求出输入的数的逆序对数(貌似方法很多吧(分治+归并排序),线段树这个方法过于精妙)

线段树的方法是因为这些数字大小是0-n-1,然后输入一个数字的时候,逆序对数就是前面已经输入的比他大的数字的个数,所以可以每输入一个数字a,用它的大小对应的区间做出修改,就是等同于单点修改,在a点+1,然后更新区间,每次输入一个数字b的时候,就是求b-n-1这个区间里的和(这个区间里出现过的数组那个点就会+1)

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
#define   MAX       10000+5
#define   MAXN      100000+5
#define   lson      l,m,rt<<1
#define   rson      m+1,r,rt<<1|1
#define   lrt       rt<<1
#define   rrt       rt<<1|1
#define   LL        long long
#define   ull       unsigned long long
#define   mem0(x)   memset(x,0,sizeof(x))
#define   mem1(x)   memset(x,-1,sizeof(x))
#define   meminf(x) memset(x,INF,sizeof(x))
#define   lowbit(x) (x&-x)

const int    mod   = 1000000007;
const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;

//读入外挂
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int a[MAX];
int sum[MAX<<2];

void pushup(int rt){
    sum[rt]=sum[lrt]+sum[rrt];
}

void update(int p,int l,int r,int rt){
    if(l==r){
        sum[rt]++;
        return;
    }
    int m=l+(r-l)/2;
    if(p<=m) update(p,lson);
    else update(p,rson);
    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-l)/2;
    int ans=0;
    if(L<=m) ans+=query(L,R,lson);
    if(R>m) ans+=query(L,R,rson);
    return ans;
}
int main(){
    int n;
    while(~scanf("%d",&n)){
        int cnt=0;
        mem0(sum);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            cnt+=query(a[i],n-1,0,n-1,1);
            update(a[i],0,n-1,1);
        }
        int mini=INF;
        for(int i=0;i<n;i++){
            cnt+=(n-1-2*a[i]);
            mini=min(mini,cnt);
        }
        printf("%d\n",mini);
    }
    return 0;
}

hdu 2795

题意:给你一个h*w的木板,然后上面可以贴海报,海报是1*L的,然后每次都贴最上面的最左边的空位,问你每个海报贴在第几行

题解:我一开始都看不出是线段树啊,太弱了,h可以是10^9,然后吓住了,看了notonlysuccess写的blog,想想也是啊,每行都是一样宽,如果n行放不下,那么h行也放不下,所以h完全可以变成n,然后就是20W的数据量,就是维护每行的最大值

int maxv[MAX<<2];
int h,w,n;
int cnt;

void pushup(int rt){
    maxv[rt]=max(maxv[lrt],maxv[rrt]);
}

void build(int l,int r,int rt){
    if(l==r){
        maxv[rt]=w;
        return;
    }
    int m=l+(r-l)/2;
    build(lson);
    build(rson);
    pushup(rt);
}

void query(int a,int l,int r,int rt){
    if(maxv[rt]<a) return;
    if(cnt!=-1) return;
    if(l==r){
        maxv[rt]-=a;
        cnt=l;
        return;
    }
    int m=l+(r-l)/2;
    query(a,lson);
    query(a,rson);
    pushup(rt);
}

int main(){
    while(~scanf("%d%d%d",&h,&w,&n)){
        if(h>n) h=n;
        build(1,n,1);
        while(n--){
            int a;
            scanf("%d",&a);
            cnt=-1;
            query(a,1,h,1);
            printf("%d\n",cnt);
        }
    }
    return 0;
}


poj 2828

题意:很多人插队,每个人有一个val,然后插在第几个人后面,最后输出这一列人的每个人的val

题解:这题难点在于想,我也是看了题解(哎虽然刷的是线段树专题,但是很多题完全想不到线段树啊,看来还是脑洞不行)

这题就是先输入,然后倒着更新,因为最后一个人的位置是确定的(pos+1),然后这样想,更新前面前面插队的人的时候,在他后面插队的对他没有影响,然后把后面插队的(已经插入线段树中的)去掉,在这些空位里,这个人也是在pos+1的位置上,然后开个sum数组记录这个区间内的空位,然后更新位置时候,如果这个位置大于左儿子的空位,那么就插入右儿子(需要减去左儿子的空位数)

int pos[MAX],val[MAX];
int sum[MAX<<2];
int col[MAX<<2];
int n;

void pushup(int rt){
    sum[rt]=sum[lrt]+sum[rrt];
}

void build(int l,int r,int rt){
    if(l==r){
        sum[rt]=1;
        return ;
    }
    mid;
    build(lson);
    build(rson);
    pushup(rt);
}

void update(int p,int v,int l,int r,int rt){
    if(l==r){
        sum[rt]=0;
        col[rt]=v;
        return;
    }
    mid;
    if(p<=sum[lrt]) update(p,v,lson);
    else update(p-sum[lrt],v,rson);
    pushup(rt);
}

void query(int l,int r,int rt){
    if(l==r){
        printf("%d",col[rt]);
        if(l!=n) printf(" ");
        else printf("\n");
        return;
    }
    mid;
    query(lson);
    query(rson);
}

int main(){
    while(~scanf("%d",&n)){
        for(int i=0;i<n;i++) scanf("%d%d",&pos[i],&val[i]);
        build(1,n,1);
        for(int i=n-1;i>=0;i--) update(pos[i]+1,val[i],1,n,1);
        query(1,n,1);
    }
    return 0;
}

poj  3667

题意:给你n个房间,然后一开始都是空的,然后有长度为d的队伍要住进去,输出开始房间序号(要最小),不存在就输出0,还可以把一段房间内住的人都清空。

题解:显然是线段树了,如何判断一个区间内最长的连续的空房间是否大于a,需要参数sum(区间内最大连续个数),lsum(从最左端开始的最长连续个数),rsum(从右端开始的最长连续个数)

因为一个区间的最大连续个数sum[rt]=max(rsum[lrt]+lsum[rrt],max(sum[lrt],sum[rrt]));

然后还需要一个cover数组来记录当前区间是否被覆盖(住人)

这种需要前缀后缀的,求连续的线段树我还是不行啊,还得多练习,不能只会做万人轮的水题

int cover[MAX<<2],sum[MAX<<2],lsum[MAX<<2],rsum[MAX<<2];

void pushdown(int rt,int m){
    if(cover[rt]!=-1){
        cover[lrt]=cover[rrt]=cover[rt];
        if(cover[rt]==0){
            lsum[lrt]=rsum[lrt]=sum[lrt]=m-(m>>1);
            lsum[rrt]=rsum[rrt]=sum[rrt]=m>>1;
        }
        else{
            lsum[lrt]=rsum[lrt]=sum[lrt]=lsum[rrt]=rsum[rrt]=sum[rrt]=0;
        }
        cover[rt]=-1;
    }
}

void pushup(int rt,int m){
    lsum[rt]=lsum[lrt];
    rsum[rt]=rsum[rrt];
    if(lsum[rt]==m-(m>>1)) lsum[rt]+=lsum[rrt];
    if(rsum[rt]==m>>1) rsum[rt]+=rsum[lrt];
    sum[rt]=max(rsum[lrt]+lsum[rrt],max(sum[lrt],sum[rrt]));
}

void build(int l,int r,int rt){
    cover[rt]=-1;
    sum[rt]=lsum[rt]=rsum[rt]=r-l+1;
    if(l==r) return;
    mid;
    build(lson);
    build(rson);
}

void update(int L,int R,int c,int l,int r,int rt){
    if(L<=l&&r<=R){
        cover[rt]=c;
        if(cover[rt]==0) sum[rt]=lsum[rt]=rsum[rt]=r-l+1;
        else sum[rt]=lsum[rt]=rsum[rt]=0;
        return;
    }
    pushdown(rt,r-l+1);
    mid;
    if(L<=m) update(L,R,c,lson);
    if(R>m) update(L,R,c,rson);
    pushup(rt,r-l+1);
}

int query(int a,int l,int r,int rt){
    if(l==r) return 1;
    pushdown(rt,r-l+1);
    mid;
    if(sum[lrt]>=a) return query(a,lson);
    else if(rsum[lrt]+lsum[rrt]>=a) return m-rsum[lrt]+1;
    else query(a,rson);
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    build(1,n,1);
    mem1(cover);
    while(m--){
        int op;
        scanf("%d",&op);
        if(op==1){
            int a;
            scanf("%d",&a);
            if(sum[1]<a) printf("0\n");
            else{
                int pos=query(a,1,n,1);
                printf("%d\n",pos);
                update(pos,pos+a-1,1,1,n,1);
            }
        }
        else{
            int a,b;
            scanf("%d%d",&a,&b);
            update(a,a+b-1,0,1,n,1);
        }
    }
    return 0;
}



你可能感兴趣的:(ACM)