經過了這幾天的學習---線段樹---收穫着實不小,不過解稍微難一點的題還是會有超時的可能,但也算是入門了。我記得在這段時間的學習中,我保持了一個良好的習慣---剛開始的時候不要在網上搜解題報告。這樣可以訓練你的思考能力,有時候肯能還會和別人的不一樣,學習起來就不是那麼死板了。這幾天的學習大致可以分爲4個階段(區間統計,單點更新,區間更新,區間合併,掃描線(這個只瞭解到了初級的))。
這類型的題目不對節點更新,只是實現的統計的功能(有時候也會伴隨着一些更新)。比如統計一個區間的最大值(最小值),統計逆序數(當然樹狀數組更好一些)等這個題一般的做法是在建樹的同時,利用回溯的過程就把區間的信息統計出來。這類題適合於初學者。
其實單點更新顧名思義就是每次操作的時候對葉子節點操作,但是常規的訪問都是以區間的形式。做這類題的時候只需要在更新的時候用update(index)實現更新就可以了,沿着葉子節點向上更新,用遞歸實現比較簡單。這類題適合於初學者。
#include<stdio.h>
#define MAXN 200005
struct Tree
{
int Max,l,r,m;
}ar[4*MAXN];
int n,m,h[MAXN];
int BuildTree(int index,int begin,int end)
{
int x,y;
ar[index].l=begin;ar[index].r=end;
ar[index].m=(begin+end)>>1;
if(begin<end)
{
x=BuildTree(2*index,begin,ar[index].m);
y=BuildTree(2*index+1,ar[index].m+1,end);
ar[index].Max=x>y?x:y;
}
else
ar[index].Max=h[begin];
return ar[index].Max;
}
int GetMax(int index,int begin,int end)
{
if(ar[index].l==begin && ar[index].r==end)
{
return ar[index].Max;
}
else if(begin>ar[index].m)
{
return GetMax(index*2+1,begin,end);
}
else if(end<=ar[index].m)
{
return GetMax(index*2,begin,end);
}
int x,y;
x=GetMax(index*2,begin,ar[index].m);
y=GetMax(index*2+1,ar[index].m+1,end);
return x>y?x:y;
}
void ChangeTree(int index,int x,int y)
{
if(ar[index].Max<y)
{
ar[index].Max=y;
}
if(ar[index].l<ar[index].r)
{
if(x<=ar[index].m)
{
ChangeTree(index*2,x,y);
}
else
{
ChangeTree(index*2+1,x,y);
}
}
}
void init()
{
int i;
for(i=1;i<=n;i++)
{
scanf("%d",&h[i]);
}
BuildTree(1,1,n);
}
void make()
{
char op[10];
int x,y;
while(m--)
{
scanf("%s%d%d",op,&x,&y);
if(op[0]=='Q')
{
printf("%d\n",GetMax(1,x,y));
}
else
{
ChangeTree(1,x,y);
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
make();
}
return 0;
}
相應的練習題:
hdu 1166 敵兵佈陣
剛開始的時候,可能有人會想到是否可以用單點更新的思想來做呢?按理來說是可以的,但是有時候往往我們代碼的效率就不夠好了。這樣想,我們可以之間更新區間,但是當我們訪問區間下面的節點的時候,不是已經改變了嗎?所以我們還得在查詢的時候,相應的更新。也叫做延遲更新。這類題適合於中等偏下的題,花不了多少時間。
相應的練習題:
hdu 4107 Gangster
hdu 3397 Sequence operation
這類題最常見的題是:求一個區間連續的一些性質,比如1-n數組裏,要麼放黑棋,要麼放白棋,然後給你一個詢問區間[i,j],問你這個區間裏黑棋連續最多的爲多少。比如最大連續上升(很多時候有伴隨着一些改變)等。常見的解法是:在節點裏添加連個變量,l,r分別記錄,左右連續的個數,然後在依次不斷的向上,不斷的更新。
這類題比較多,給一個題的代碼:
hdu 3308 LCIS
#include<stdio.h>
#define MAXN 100005
struct data
{
int l1,l2,r2,r1;
};
struct node
{
int begin,end,mid;
int num;
data p;
}tree[MAXN<<2];
int n,m,st[MAXN],ans;
void SetValue(data &p,int l1,int l2,int r2,int r1)
{
p.l1=l1;
p.l2=l2;
p.r2=r2;
p.r1=r1;
}
int Max(int x,int y)
{
return x>y?x:y;
}
void update(int index)
{
int x=0;
data p=tree[index<<1].p,q=tree[index<<1 | 1].p;
if(st[p.r1]<st[q.l1])
{
x=q.l2-p.r2+1;
}
tree[index].num=Max(x,Max(tree[index<<1].num,tree[index<<1 | 1].num));
if(p.l2+1==q.l1 && st[p.l2]<st[q.l1])
{
p.l2=q.l2;
}
if(p.r1+1==q.r2 && st[p.r1]<st[q.r2])
{
q.r2=p.r2;
}
p.r2=q.r2;
p.r1=q.r1;
tree[index].p=p;
}
void BuildTree(int index,int begin,int end)
{
tree[index].begin=begin;tree[index].end=end;
tree[index].mid=(begin+end)>>1;
if(begin<end)
{
int x=0;
BuildTree(index<<1,begin,tree[index].mid);
BuildTree(index<<1 | 1,tree[index].mid+1,end);
update(index);
}
else
{
tree[index].num=1;
scanf("%d",&st[end]);
SetValue(tree[index].p,end,end,end,end);
}
}
void Insert(int index,int x)
{
if(tree[index].begin==tree[index].end)
{
return;
}
if(x<=tree[index].mid)
{
Insert(index<<1,x);
}
else
{
Insert(index<<1 | 1,x);
}
update(index);
}
data get(int index,int begin,int end)
{
data p,q;
if(tree[index].begin==begin && tree[index].end==end)
{
if(ans<tree[index].num)
{
ans=tree[index].num;
}
return tree[index].p;
}
if(end<=tree[index].mid)
{
return get(index<<1,begin,end);
}
else if(begin>tree[index].mid)
{
return get(index<<1 | 1,begin,end);
}
p=get(index<<1,begin,tree[index].mid);
q=get(index<<1 | 1,tree[index].mid+1,end);
int x=0;
if(st[p.r1]<st[q.l1])
{
x=q.l2-p.r2+1;
}
ans=Max(x,ans);
if(p.l2+1==q.l1 && st[p.l2]<st[q.l1])
{
p.l2=q.l2;
}
if(p.r1+1==q.r2 && st[p.r1]<st[q.r2])
{
q.r2=p.r2;
}
p.r2=q.r2;
p.r1=q.r1;
return p;
}
void make()
{
BuildTree(1,0,n-1);
int x,y;
char op[5];
while(m--)
{
scanf("%s%d%d",op,&x,&y);
if(op[0]=='Q')
{
ans=0;
get(1,x,y);
printf("%d\n",ans);
}
else
{
st[x]=y;
Insert(1,x);
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
make();
}
return 0;
}
相應的練習題:
hdu 3911 Back And White
hdu 4046 Panda
這一階段主要求的是一些矩形的面積並,周長並,有時候也會有體積並。做這類的題,一般會用到離散化的思想。比如你要插入[4999,6000]這個區間到線段樹中,那麼你只需要把4999和6000分別映射到兩個更小的數x,y然後插入。然後一次掃描線段就可以了。
今天上午寫了一個代碼:
hdu 1255 覆蓋的面積
#include<stdio.h>
#include<cmath>
#include<algorithm>
#define MAXN 2005
using namespace std;
double flag[MAXN];
struct data
{
double y1,y2,x,f;
}line[MAXN];
struct node
{
int begin,end,mid,cover;
double x;
}st[MAXN<<2];
int n;
int cmpdouble(double x,double y)
{
return x<y;
}
int cmpd(data p,data q)
{
return p.x<q.x;
}
void BuildTree(int t,int begin,int end)
{
st[t].begin=begin;st[t].end=end;
st[t].mid=(begin+end)>>1;
st[t].cover=0;
st[t].x=0;
if(begin>=end-1)
return;
BuildTree(t<<1,begin,st[t].mid);
BuildTree(t<<1 | 1,st[t].mid,end);
}
void init()
{
int i,l,r;
double x1,x2,y1,y2;
for(i=0;i<n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
l=i<<1;r=l+1;
flag[l]=y1;
line[l].x=x1;
line[l].y1=y1;
line[l].y2=y2;
line[l].f=1;
flag[r]=y2;
line[r].x=x2;
line[r].y1=y1;
line[r].y2=y2;
line[r].f=-1;
}
n=r;
sort(flag,flag+n+1,cmpdouble);
sort(line,line+n+1,cmpd);
BuildTree(1,0,n);
}
int search(double x)
{
int l=0,r=n,mid;
double y;
while(l<=r)
{
mid=(l+r)>>1;
if(fabs(flag[mid]-x)<1e-8)
return mid;
else if(flag[mid]<x)
{
l=mid+1;
}
else
{
r=mid-1;
}
}
return -1;
}
double update(int t,int begin,int end,double x,int f)
{
if(begin>=st[t].end || end<=st[t].begin)
return 0;
if(st[t].begin+1==st[t].end)
{
double ans;
if(st[t].cover>1)
{
ans=(x-st[t].x)*(flag[st[t].end]-flag[st[t].begin]);
st[t].x=x;
st[t].cover+=f;
return ans;
}
else
{
st[t].cover+=f;
st[t].x=x;
return 0;
}
}
return update(t<<1,begin,end,x,f)+
update(t<<1 | 1,begin,end,x,f);
}
void make()
{
double sum=0;
int i,l,r;
for(i=0;i<n;i++)
{
l=search(line[i].y1);
r=search(line[i].y2);
sum+=update(1,l,r,line[i].x,line[i].f);
}
printf("%.2lf\n",sum);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init();
make();
}
return 0;
}
其實還可以優化,留給自己慢慢完成!!!!
相應的練習:
hdu 3642 Get The Treasury(體積合併)
這幾天的練習總體說來效果還是可以,至少解決了很多自己不能完成的東西。其實還有其他的線段樹解法,它不是常規的。比如:hdu 4007 Dave。
第一次近距離接觸線段樹,希望自己後面能更好的理解線段樹。