1. 程式人生 > >ZOJ#1610-線段樹區間更新

ZOJ#1610-線段樹區間更新

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; }