poj2528 Mayor's posters(线段树的离散化)

 

题目链接:http://poj.org/problem?id=2528

分析:其实离散化就是将一个很大的区间映射为一个很小的区间,而不改变原有的大小覆盖关系。但是注意简单的离散化可能  会出现错误,给出下面两个简单的例子应该能体现普通离散化的缺陷: 例子一:1-10 1-4 5-10 例子二:1-10 1-4 6-10 普通离散化后都变成了[1,4][1,2][3,4] 线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢? 例子一是完全被覆盖掉了,而例子二没有被覆盖  解决的办法则是对于距离大于1的两相邻点,中间再插入一个点,本题还用到了Lazy标记的思想。

代码:

 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e4+10;
int s[maxn],e[maxn],lisan[4*maxn],t,n,tmp,vis[maxn],ans;
struct node{
    int l,r,v;
}tree[16*maxn];
 
int bin(int x){
    int l=1,r=tmp-1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(lisan[mid]==x) return mid;
        else if(x<lisan[mid]) r=mid-1;
        else l=mid+1;
    }
}
 
void build(int l,int r,int id){
    tree[id].l=l,tree[id].r=r,tree[id].v=-1;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(l,mid,2*id);
    build(mid+1,r,2*id+1);
}
 
void update(int l,int r,int id,int col){
    if(l<=tree[id].l && tree[id].r<=r){
        tree[id].v=col;
        return ;
    }
    if(tree[id].v!=-1) {
        tree[2*id].v=tree[2*id+1].v=tree[id].v;
        tree[id].v=-1;
    }
    int mid=(tree[id].l+tree[id].r)>>1;
    if(r<=mid) update(l,r,2*id,col);
    else if(l>mid) update(l,r,2*id+1,col);
    else update(l,mid,2*id,col),update(mid+1,r,2*id+1,col);
}
 
void query(int l,int r,int id){
    if(tree[id].v!=-1){
        if(!vis[tree[id].v]){
            ans++;
            vis[tree[id].v]=1;
        }
        return ;
    }
    if(l==r) return ;
    int mid=(l+r)>>1;
    query(l,mid,2*id);
    query(mid+1,r,2*id+1);
}
 
int main(){
    ///std::ios::sync_with_stdio(false);
    cin>>t;
    while(t--){
        memset(vis,0,sizeof(vis));
        cin>>n;
        for(int i=1;i<=n;i++) cin>>s[i]>>e[i];
        for(int i=1;i<=n;i++) lisan[i]=s[i];
        for(int i=n+1;i<=2*n;i++) lisan[i]=e[i-n];
        ///离散化
        sort(lisan+1,lisan+2*n+1);
        int tot=1;
        lisan[tot++]=lisan[1];
        for(int i=1;i<2*n;i++)
            if(lisan[i+1]!=lisan[i]) lisan[tot++]=lisan[i+1];
        tmp=tot-1;
        for(int i=1;i<tot;i++)
            if(lisan[i+1]-lisan[i]!=1) lisan[++tmp]=lisan[i]+1;
        sort(lisan+1,lisan+tmp);
 
        build(1,tmp-1,1);
        for(int i=1;i<=n;i++){
            int L=bin(s[i]),R=bin(e[i]); //二分
            update(L,R,1,i);
        }
        
        query(1,tmp-1,1);
        cout<<ans<<endl;
        ans=0;
    }
}

线段树的应用

维护区间最大值

void pushup( ll r ) {
    root[r] = max(root[ls],root[rs]);
}

void updata( ll r, ll le, ll ri, ll pos, ll w ) {
    if( le == ri ) {
        root[r] = w;
        return;
    }
    ll mid = (le+ri)/2;
    if( pos <= mid ) {
        updata(ls,le,mid,pos,w);
    }
    if( mid < pos ) {
        updata(rs,mid+1,ri,pos,w);
    }
    pushup(r);
}

 

去重+离散化

//一个n*n的图中有效贡献点有限,可这样离散,注意要写行列贡献相同需去重
        for( ll i = 1; i <= n; i ++ ) {
            scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].w);
            dis[i] = 0, num[i] = a[i].y;
        }
        sort(num+1,num+n+1);
        sz = unique(num+1,num+n+1) - (num+1);  //去重
        sort(a+1,a+n+1,cmp);
        a[n+1].x = a[n].x + 1;
        build(1,1,sz);    //建树

 

 求图贡献最大

//一段区间最大贡献
ll query( ll r, ll le, ll ri, ll L, ll R ) {
        if( R < le || ri < L ) {
        return 0;
    }
    if( L <= le && ri <= R ) {
        return root[r];
    }
    ll MAX = 0;
    ll mid = (le+ri)/2;
    if( L <= mid ) {
        MAX = max(MAX,query(ls,le,mid,L,R));
    }
    if( mid < R ) {
        MAX = max(MAX,query(rs,mid+1,ri,L,R));
    }
    return MAX;
}

           dis[i] = query(1,1,sz,1,le-1) + a[i].w;
           if( a[i].x != a[i+1].x ) {
                if( dis[i] > root[pos[le]] ) {
                    updata(1,1,sz,le,dis[i]);
                }
                for( ll i = 1; i <= cnt; i ++ ) {
                    if( dt[i] > root[pos[st[i]]] ) {
                        updata(1,1,sz,st[i],dt[i]);
                    }
                }
                cnt = 0;
            } else {
                cnt ++;
                st[cnt] = le, dt[cnt] = dis[i];
            }
            ans = max(ans,dis[i]);

 

 

 区间最大值


void build(int l, int r, int n)
{
    s[n].l = l;
    s[n].r = r;
    s[n].maxx = 0;
    s[n].minn = N;
    if(l == r)
    {
        s[n].maxx = s[n].minn = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, n<<1);
    build(mid + 1, r, n<<1|1);
    s[n].maxx = max(s[n<<1].maxx, s[n<<1|1].maxx);
    s[n].minn = min(s[n<<1].minn, s[n<<1|1].minn);
}
void query(int l, int r, int n)
{
    if(s[n].l == l && s[n].r == r)
    {
        ans_x = max(ans_x, s[n].maxx);
        ans_y = min(ans_y, s[n].minn);
        return;
    }
    int mid = (s[n].l + s[n].r) >> 1;
    if(r <= mid)
        query(l, r, n<<1);
    else if(l > mid)
        query(l, r, n<<1|1);
    else
    {
        query(l, mid, n<<1);
        query(mid+1, r, n<<1|1);
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值