kuangbin专题七 线段树【从入门到熟练】【4题】

【POJ 2528 Mayor's posters】

每个海报会覆盖一段连续的区间,所以这是个区间覆盖问题,可以用线段树。

但硬上nlogm虽然不会tle,但会mle,所以要离散化。

在[1,10000000]的这个瓷砖里,只有10000级别的修改,我们可以想象出整个区间可以被划分为多个被cover情况【一致】的小区间。那什么样的小区间被cover情况一致呢,所有端点排序后相邻端点构成的区间被cover情况是一致的,为什么呢?因为每次的覆盖都必定是两两端点间的。

这样的话每次cover就是cover这个海报start所代表的区间到end所代表的区间。

【注意要从后往前贴海报】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<string>
#include<stack>
#include<map>
#include<iomanip> 
#include<algorithm>
#include<vector>
#define INF 2e9
#define MAXN 20010
#define maxnode 100000
#define ll long long
#define lowbit(x) (x&(-x))
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
using namespace std;

int l[MAXN],r[MAXN],a[MAXN];
map<int,int> mp;

struct node{
    int l,r;
    bool covered;
    int d;//有没有被cover,有多少部分被cover不重要,只关心这个区间有没有被【完全】cover
    node *ls,*rs; 
}pool[MAXN*4];

int top;
node* buildT(int l,int r){
    node* p = pool + (++top);
    p->l=l; p->r=r;
    p->covered=false;
    p->d=0;
    if( l==r ) return p;
    p->ls = buildT(l,(l+r)/2);
    p->rs = buildT( (l+r)/2+1,r );
    return p; 
}

void update(node* p,int l,int r){//覆盖上这个区间
    if( p->l==l && p->r==r ){
        p->d=1;
        return;
    }
    int mid=(p->l+p->r)/2;
    if( mid>=r ) update(p->ls,l,r);
    else if( l>mid ) update(p->rs,l,r);
    else{
        update(p->ls,l,mid);
        update(p->rs,mid+1,r);
    }
    p->d = p->d | ( p->ls->d&p->rs->d);
}

bool query(node* p,int l,int r){//询问这个区间有没有被完全覆盖
    if( p->l==l && p->r==r ) return p->d;
    if( p->d ) return true;//p->d为1的话,省去了pushdown == 况且p->d为0的话也不能pushdown 
    int mid=(p->l+p->r)/2;
    if( mid>=r ) return query(p->ls,l,r);
    else if( l>mid ) return query(p->rs,l,r);
    return query(p->ls,l,mid)&query(p->rs,mid+1,r);  
}

int main(){
    //ios::sync_with_stdio(false);
    int t; scanf("%d",&t);
    while(t--){
        int n; scanf("%d",&n);
        int cnt=0,id=0;
        for(int i=1;i<=n;i++){
             scanf("%d%d",&l[i],&r[i]);
            a[++id]=l[i]; a[++id]=r[i];
        }
        sort(a+1,a+1+id);
        int num=unique(a+1,a+1+id)-(a+1);
        for(int i=1;i<=num;i++) mp[ a[i] ] = i;
        
        top=0; 
        node* root = buildT(1,num);
        for(int i=n;i>=1;i--){
            if( !query(root,mp[ l[i] ],mp[ r[i] ]) ) cnt++;
            update(root,mp[ l[i] ],mp[ r[i] ]);
        }
        
        printf("%d\n",cnt);
    }
    
    return 0;
}
View Code

【HDU 2698 Just a Hook】

对于线段树我想的是每个node对应一个区间 [l,r] ,并且会用d存一个这个区间里所记录的值,那这涉及到一个问题是对于终止结点该怎么修改,修改后要怎么反馈到上层结点(即push up)。

然后更新d值我会从两个方面考虑,一就是根据题意随便搞,二是考虑下贡献。对于这题的话就是随便搞,然后记得加一个tag(延迟更新)记录这个区间是什么颜色,以后询问或更新以下的区间的话要pushdown下去。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<string>
#include<stack>
#include<map>
#include<iomanip> 
#include<algorithm>
#include<vector>
#define INF 2e9
#define MAXN 200010
#define maxnode 100000
#define ll long long
#define lowbit(x) (x&(-x))
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
using namespace std;

struct node{
    int l,r;
    int d,tag;
    node *ls,*rs;
}pool[4*MAXN];

int top;
node* buildT(int l,int r){
    node* p = pool + (++top);
    p->l=l; p->r=r;
    p->tag=0;//以下的都是tag类型的hook
    if( l==r ){
        p->d=1;
        return p;
    }
    int mid = (l+r)/2;
    p->ls = buildT(l,mid);
    p->rs = buildT(mid+1,r);
    p->d = p->ls->d+p->rs->d;
    return p;
}

int getData(node* p){
    if(p->tag==0) return p->d;
    return (p->r-p->l+1)*p->tag;
}

void pushdown(node* p){
    if(p->tag==0) return; 
    p->ls->tag = p->tag;
    p->rs->tag = p->tag;
    p->d = getData(p);
    p->tag=0;
}

void update(node* p,int l,int r,int type){
    if( p->l==l && p->r==r ){
        p->tag=type;
        return;
    }
    pushdown(p);//顺序重要 
    int mid=(p->r+p->l)/2;
    if( r<=mid ) update(p->ls,l,r,type);
    else if( l>mid ) update(p->rs,l,r,type);
    else{
        update(p->ls,l,mid,type);
        update(p->rs,mid+1,r,type);
    }
    p->d = getData(p->ls)+getData(p->rs);
}

int query(node* p,int l,int r){
    if( p->l==l && p->r==r ) return getData(p);
    pushdown(p);
    int mid = (p->l+p->r)/2;
    if( r<=mid ) return query(p->ls,l,r);
    else if( l>mid ) return query(p->rs,l,r);
    return query(p->ls,l,mid)+query(p->rs,mid+1,r);
}

int main(){
    //ios::sync_with_stdio(false);
    int t; scanf("%d",&t);
        
    for(int i=1;i<=t;i++){    
        int n,q; scanf("%d%d",&n,&q);
        top=0;
        node* root = buildT(1,n);
        for(int j=1;j<=q;j++){
            int x,y,z; scanf("%d%d%d",&x,&y,&z);
            update(root,x,y,z);
        }
        
        printf("Case %d: The total value of the hook is %d.\n",i,query(root,1,n));
    }
    

    
    return 0;
}
View Code

【ZOJ 1610 Count the Colors】

这题算是我很喜欢的一道线段树题了, 没有被虐就没有热爱。感觉这题好是好在在线段树里加入了【暴力】,就很棒

原本想的是logn回答的话,那就是要用logn级别个终止结点拼凑出答案,那每个结点岂不是都要存一下有哪些颜色段,而且颜色段分别是什么?但这样太复杂了

需要敏锐地捕捉到对于每个data set只会有一次询问,那么O(n)的复杂度处理询问是可以接受的喂!

想到这点还不够,还要能想到那询问的时候把所有颜色都推到【叶子节点】看叶子结点是什么颜色就好了啊,就这么做完了。

而且注意到这里有个好处是由于dfs那么叶子节点的访问顺序一定是[1,1] , [2,2] , [3,3] , ... , [n,n],借此可以算出整个颜色段

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<string>
#include<stack>
#include<map>
#include<iomanip> 
#include<algorithm>
#include<vector>
#define INF 2e9
#define MAXN 200010
#define maxnode 100000
#define ll long long
#define lowbit(x) (x&(-x))
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
using namespace std;

struct node{
    int l,r;
    int tag;
    node *ls,*rs;
}pool[4*MAXN];

int ans[MAXN];

int top;
node* buildT(int l,int r){
    node* p = pool + (++top);
    p->l=l; p->r=r;
    p->tag=-1;
    if( l==r ) return p;
    int mid = (l+r)>>1;
    p->ls = buildT( l,mid );
    p->rs = buildT( mid+1,r );
    return p;
}

void pushdown(node* p){
    if( p->tag==-1 ) return;
    
    p->ls->tag=p->tag; p->rs->tag=p->tag;
    p->tag=-1; 
}

void update(node* p,int l,int r,int dx){
    if( p->l==l && p->r==r ){
        p->tag = dx;
        return;
    }
    int mid = (p->l+p->r)>>1;
    pushdown(p);
    if( r<=mid ) update(p->ls,l,r,dx);
    else if( l>mid ) update(p->rs,l,r,dx);
    else{
        update(p->ls,l,mid,dx);
        update(p->rs,mid+1,r,dx);
    }
}

int last;
void query(node* p,int l,int r){
    if( l==r ){
        if( p->tag!=-1 && p->tag!=last ) ans[ p->tag ]++;
        last=p->tag;
        return;
    }
    pushdown(p);
    int mid = (p->l+p->r)>>1;
    if( r<mid ) query(p->ls,l,r);
    else if( l>mid ) query(p->rs,l,r);
    else{
        query(p->ls,l,mid);
        query(p->rs,mid+1,r);
    } 
    
}

int main(){
    //ios::sync_with_stdio(false);
    int n;
    while( scanf("%d",&n)!=EOF ){
        top=0;
        node* root = buildT(1,8000);
        for(int i=1;i<=n;i++){
            int x1,x2,c; scanf("%d%d%d",&x1,&x2,&c);
            update(root,x1+1,x2,c);        
        }
 
        memset(ans,0,sizeof(ans));
        last=-1;
        query(root,1,8000);
        for(int i=0;i<=8000;i++){
            if( ans[i] ) printf("%d %d\n",i,ans[i]);
        }
        

        printf("\n");
    }

    
    return 0;
}
View Code

【HDU 4027 Can you answer these queries?】

这题是要想到一个比较妙的性质然后就能做了,不过这题坑点挺多的,真是难度不够坑来凑。。

主要还是不知道要怎么更新一个区间,因为这个区间里的数都square root一下让区间和减小多少是不好说的。对于这种题就是要多想想,想到一个性质然后就能做了。关键点在于一个数square root最多7下就变成1了,而1以后就不影响了。所以更新的时候判断下当前区间是否都是1,如果都是1直接return(都是1的话就是区间和等于区间长度);不然的话不管它是不是终止结点(直到叶子结点为止)都继续更新下去。也就是说前几次都是O(n)代价更新,那为什么不会超时呢?因为当更新次数足够多以后,再更新的时候就会发现当前区间都是1,那么就自动变成logn更新了!O(n)线性更新的总代价应该是7*n级别的,完全可以过。

坑点在输入要是lld,我就是输入成了int,然后爆成负数,之后负数做sqrt还是返回负数,所以一直都是O(n)更新,因此竟然很多次都是tle而不是wa,给了我错误导向。。还有是两艘ship输入不保证大小关系,所以可能要swap一下

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<string>
#include<stack>
#include<map>
#include<iomanip> 
#include<algorithm>
#include<vector>
#define INF 2e9
#define MAXN 200010
#define maxnode 100000
#define ll long long
#define lowbit(x) (x&(-x))
using namespace std;

ll a[MAXN];

struct node{
    int l,r;
    ll d,tag;//endurance & if all is 1
    node *ls,*rs;
}pool[4*MAXN];

int top;
node* buildT(int l,int r){
    node* p = pool + (++top);
    p->l = l; p->r = r;
    p->tag = 0;
    if( l==r ){
        p->d = a[l];
        if( a[l]==1 ) p->tag=1;
        return p;
    }
    int mid = (l+r)>>1;
    p->ls = buildT(l,mid);
    p->rs = buildT(mid+1,r); 
    p->d = p->ls->d + p->rs->d;
    p->tag = (p->ls->tag&&p->rs->tag);
    return p;
}

void update(node* p,int l,int r){
    if( p->l==l && p->r==r && p->tag ){//all is one
        return;
    }
    if(p->l==p->r){//leaves
        p->d = long( sqrt(p->d) );
        if( p->d==1 ) p->tag=1;
        return;
    }
    int mid = (p->l+p->r)>>1;
    if( r<=mid ) update(p->ls,l,r);
    else if( l>mid ) update(p->rs,l,r);
    else{
        update(p->ls,l,mid);
        update(p->rs,mid+1,r);
    }
    p->d = p->ls->d + p->rs->d;
    p->tag = p->ls->tag && p->rs->tag;
    return;
}

ll query(node* p,int l,int r){
    if( p->l==l && p->r==r ) return p->d;
    int mid = (p->l+p->r)>>1;
    if( r<=mid ) return query(p->ls,l,r);
    else if( l>mid ) return query(p->rs,l,r);
    return query(p->ls,l,mid)+query(p->rs,mid+1,r);
}

int main(){
    //ios::sync_with_stdio(false);
    int n,tc=0;
    while( scanf("%d",&n)!=EOF ){
        printf("Case #%d:\n",++tc);
        for(int i=1;i<=n;i++) scanf("%lld",a+i);
        int m; scanf("%d",&m);
        
        top=0;
        node* root = buildT(1,n);
        for(int i=1;i<=m;i++){
            int t,x,y; scanf("%d%d%d",&t,&x,&y);
            if( x>y ) swap(x,y);
            if( t==0 ) update(root,x,y);
            else printf("%lld\n",query(root,x,y));
        }
        printf("\n");
    }
    
    
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/ZhenghangHu/p/9785725.html