题目链接: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;
}