POJ2528【线段树经典染色。】

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

 

 

来一点转的线段树精髓:

1、 线段树是二叉树,且必定是平衡二叉树,但不一定是完全二叉树。

2、 对于区间[a,b],令mid=(a+b)/2,则其左子树为[a,mid],右子树为[mid+1,b],当a==b时,该区间为线段树的叶子,无需继续往下划分。

3、 线段树虽然不是完全二叉树,但是可以用完全二叉树的方式去构造并存储它,只是最后一层可能存在某些叶子与叶子之间出现“空叶子”,这个无需理会,同样给空叶子按顺序编号,在遍历线段树时当判断到a==b时就认为到了叶子,“空叶子”永远也不会遍历到。

4、 之所以要用完全二叉树的方式去存储线段树,是为了提高在插入线段和搜索时的效率。用p*2,p*2+1的索引方式检索p的左右子树要比指针快得多。

5、线段树的精髓是,能不往下搜索,就不要往下搜索,尽可能利用子树的根的信息去获取整棵子树的信息。如果在插入线段或检索特征值时,每次都非要搜索到叶子,还不如直接建一棵普通树更来得方便。

 

 

 

主动思考,一开始用简单hash判断染色,可自己总是扯不上线段树去。

后来明白了思路,每次判断染色时便把左右区间修改为i(即第几次修改..)   【显然这样才能利用线段树的优势啊】

可离散化又出了点问题,学到一招用二分离散化,自己以前学到的那个离散化就是排好序后分别赋值i++,可距离大于1的时候+1就不好处理了,

顺便看ACRush以及别人的各种回忆录,以及在暑假做的某些题,二分或者二分的思路真的应用非常非常广,OK!

在我看来线段最精髓的地方就是pushdown,只有在查询或Update有交集的时候才会更新。。

还有一个很重要的地方就是数组开的大小,为确保自己在内存允许的情况尽量开大。

自己分析的时候难免有错。

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r

const int maxn=11111;

int col[maxn*16];//这地方必须乘的系数得万分注意
int li[maxn<<2];
int ri[maxn<<2];
bool hash[maxn<<2];//..
int ans=0;
int x[maxn<<2];
int y[maxn<<2];

void Pushdown(int rt)
{
    if(col[rt]!=-1)
    {
      col[rt<<1]=col[rt<<1|1]=col[rt];
      col[rt]=-1;
    }
}
void update(int ql,int qr,int ch,int rt,int l,int r)
{
    if(ql<=l&&qr>=r)
    {
        col[rt]=ch;
        return;
    }

    Pushdown(rt);
    int m=(l+r)>>1;
    if(ql<=m)
        update(ql,qr,ch,lson);
    if(qr>m)
        update(ql,qr,ch,rson);
}
void query(int ql,int qr,int rt,int l,int r)
{
    if(col[rt]!=-1)
    {
        if(hash[col[rt]]==false)//曾错在这地方 一种颜色的统计一次就够了。
        {
             ans++;
             hash[col[rt]]=true;
        }
        col[rt]=-1;
        return;
    }

    if(l==r) return;//要这一步其实是必须的 特殊情况是考虑分叉

    Pushdown(rt);//这一步应该有或没有都能AC。

    int m=(l+r)>>1;
    if(ql<=m)
     query(ql,qr,lson);
    if(qr>m)
     query(ql,qr,rson);
}
int Bin(int ans,int x[],int l,int r)
{
    while(l<r)
    {
      int m=(l+r)>>1;
      if(x[m]==ans)
        return m;
      if(ans<=x[m])
        r=m-1;
      else l=m+1;
    }
    return l;
}

int main()
{
    int case_num;
    scanf("%d",&case_num);
    while(case_num--)
    {
        ans=0;
        memset(col,-1,sizeof(col));
        memset(hash,false,sizeof(hash));
        int n;
        scanf("%d",&n);
        int cnt=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&li[i],&ri[i]);
            x[cnt++]=li[i];
            x[cnt++]=ri[i];
        }

        //离散化。 应该又学到一招  不一定靠id 使用二分
        sort(x+1,x+cnt);
        int mm=cnt;
        for(int i=2;i<cnt;i++)
        {
            if((x[i]-x[i-1])>1)
              x[mm++]=x[i-1]++;
        }
        sort(x+1,x+mm);

        int tt=2;
        y[1]=x[1];
        for(int i=2;i<mm;i++)
        {
            if(x[i]!=x[i-1])
              y[tt++]=x[i];
        }

        for(int i=1;i<=n;i++)
        {
            int l=Bin(li[i],y,1,tt-1);
            int r=Bin(ri[i],y,1,tt-1);
            update(l,r,i,1,1,tt-1);
        }
        query(1,tt-1,1,1,tt-1);
        printf("%d\n",ans);
    }
    return 0;
}


 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值