ZOJ#1610-線段樹區間更新
阿新 • • 發佈:2018-12-09
acm複習第一站,線段樹。處女題解就交給本校oj的題目吧。 題目挺有趣的,程式碼量適中,難度偏易,是一道練習線段樹基礎操作的好題。 ps.一定要注意,覆蓋的不是點,是區間!題目給的n不是區間大小,是運算元!
題目連結 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1610 題目大意 在一條長度為8000的線段上染色,每次將一段區間塗成某種顏色,後面染的色可以覆蓋原來染的色。詢問所有染色操作之後,各種顏色線上段表面各自形成了多少連續的區間。 資料範圍 端點,顏色,運算元,均不超過8000; 解題思路 塗色操作是中規中矩的線段樹區間覆蓋的操作,對於最後的統計,一個比較容易想到的辦法是從頭開始一段段地找出同色的連續區間,然後在這段區間對應的顏色的計數表中+1。顯然,可以直接遍歷一次線段樹出結果。但我當時給想複雜了,多維護了兩個值,搞了一個函式,詢問時能給出一個點右側最長的與該點同色的區間終點。有興趣的看看我的程式碼註釋呀。 參考程式碼
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN=8000;
int n;
struct node
{
int cover,start,well;
/*
cover是lazy標記,-1表示無標記。
start表示區間起點顏色,-1表示無顏色。
well==1表示區間同色,well==0表示區間不同色
*/
node operator + (const node &A)//結點合併
{
node P;
if (cover==A.cover) P.cover=cover;
else P.cover=-1;
if (well&&A.well&&start==A.start) P.well=1;
else P.well=0;
P.start=start;
return P;
}
};
struct pr
{
int end,color;//專用於查詢函式,end表示同色區間的右端點,color表示該區間的顏色
};
struct Tree
{
node cache[MAXN*4+10];
void build()
{
build(1,1,MAXN);
}
void build(int root,int L,int R)
{
cache[root]=(node){-1,-1,1};
if (R-L) build(root<<1,L,L+R>>1),build(root<<1|1,(L+R>>1)+1,R);
}
void setlazy(int root,int c)
{
cache[root]=(node){c,c,1};
}
void lazydown(int root)
{
if (~cache[root].cover)
{
setlazy(root<<1,cache[root].cover);
setlazy(root<<1|1,cache[root].cover);
cache[root].cover=-1;
}
}
void paint(int l,int r,int c)
{
paint(1,1,MAXN,l,r,c);
}
void paint(int root,int L,int R,int l,int r,int c)
{
if (L>=l&&R<=r) {setlazy(root,c);return;}
lazydown(root);
int M=L+R>>1;
if (l<=M&&r>=L) paint(root<<1,L,M,l,r,c);
if (l<=R&&r>M) paint(root<<1|1,M+1,R,l,r,c);
cache[root]=cache[root<<1]+cache[root<<1|1];
}
pr query(int st)
{
return query(1,1,MAXN,st);
}
pr query(int root,int L,int R,int st)
{
if (L==st&&cache[root].well) return (pr){R,cache[root].start};
//當前結點是一個完整的同色區間,且左側與待查點重合,直接返回區間右端點
int M=L+R>>1;
lazydown(root);
if (st>M) return query(root<<1|1,M+1,R,st);//若待查點在右兒子,直接遞迴右兒子
pr A;
A=query(root<<1,L,M,st);
if (A.end==M&&cache[root<<1|1].start==A.color)//如果左側同色區間到達左兒子的邊界,且與右兒子的左端點同色,那麼可以將該同色區間擴充套件到右兒子
A=query(root<<1|1,M+1,R,M+1);
return A;
}
}T;
int cnt[MAXN+10];//各個顏色的計數表
int main()
{
while (~scanf("%d",&n))
{
T.build();
memset(cnt,0,sizeof(cnt));
int l,r,c;
for (int i=1;i<=n;i++)
{
scanf("%d%d%d",&l,&r,&c);
T.paint(l+1,r,c);//將區間問題轉化為點問題
}
int st=1;pr seg;
while (st<=MAXN)
{
seg=T.query(st);
if (~seg.color) cnt[seg.color]++;
st=seg.end+1;//指標跳到該區間的末端
}
for (int i=0;i<=MAXN;i++)
if (cnt[i]) printf("%d %d\n",i,cnt[i]);
printf("\n");
}
return 0;
}