1. 程式人生 > >Count the Colors(線段樹染色)

Count the Colors(線段樹染色)

Count the Colors

Time Limit:2000MS    Memory Limit:65536KB    64bit IO Format:%lld & %llu

Description

Painting some colored segments on a line, some previously painted segments may be covered by some the subsequent ones.

Your task is counting the segments of different colors you can see at last.

Input



The first line of each data set contains exactly one integer n, 1 <= n <= 8000, equal to the number of colored segments.

Each of the following n lines consists of exactly 3 nonnegative integers separated by single spaces:

x1 x2 c

x1 and x2 indicate the left endpoint and right endpoint of the segment, c indicates the color of the segment.

All the numbers are in the range [0, 8000], and they are all integers.

Input may contain several data set, process to the end of file.

Output



Each line of the output should contain a color index that can be seen from the top, following the count of the segments of this color, they should be printed according to the color index.

If some color can't be seen, you shouldn't print it.

Print a blank line after every dataset.

Sample Input



5
0 4 4
0 3 1
3 4 2
0 2 2
0 2 3
4
0 1 1
3 4 1
1 3 2
1 3 1
6
0 1 0
1 2 1
2 3 1
1 2 0
2 3 0
1 2 1

Sample Output



1 1
2 1
3 1

1 1

0 2
1 1

線段樹區間更新的變形

題意:

在一條長度為8000的線段上染色,每次把區間[a,b]染成c顏色。顯然,後面染上去的顏色會覆蓋掉之前的顏色。

求染完之後,每個顏色線上段上有多少個間斷的區間。

用區間更新的方式,對於區間內的先不更新,當出現新的線段覆蓋的時候在pushdown,mark[]遍歷一下離根最近的color不為-1(為染色)的就行了,最後通過sum統計出從到大的就好了。

PS:開始的時候由於用來之前的部分程式碼,結果在pushdown的時候錯誤成了

tree[tmp].color +=  tree[x].color;
結果出了個Segmentation Fault 從來沒有見過的錯,結果是ZOJ越界(RE)的錯誤,查了好久。。。
//線段樹區間更新
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<iomanip>
#include<string>
#include<climits>
#include<cmath>
#define INF 0x3f3f3f3f
#define MAX 8010
#define LL long long
using namespace std;

struct Tree
{
  int  l,r;
  int color;
};
Tree tree[MAX*4];

void pushdown(LL x)  ///用於更新color陣列
{
  LL tmp = x<<1 ;
  tree[tmp].color =  tree[x].color;  ///由子節點通過增加
  tree[tmp+1].color = tree[x].color;
  tree[x].color=-1;
}
void build(int l,int r,int x)
{
  tree[x].l=l , tree[x].r=r , tree[x].color=-1;
  if(l==r)  return ;
  int tmp=x<<1;
  int mid=(l+r)>>1;
  build(l,mid,tmp);
  build(mid+1,r,tmp+1);
}

void update(int  l,int  r,int c,int x)  ///分別表示區間的左 , 右 , 增加的值  ,當前父親節點
{
  if(r<tree[x].l||l>tree[x].r)   return ;
  if(l<=tree[x].l&&r>=tree[x].r)  ///該區間為需要更新區間的子區間
  {
    tree[x].color = c;
    return ;
  }
  if(tree[x].color!=-1)  pushdown(x);  ///更新從上向下更新color
  update(l,r,c,x<<1);
  update(l,r,c,(x<<1)+1);
}

int mark[MAX<<2],coun=0; ///注意大小
void query(int  l ,int  r ,int x )
{
  if((l==tree[x].l&&r==tree[x].r && tree[x].color!=-1)  ||  tree[x].l == tree[x].r){
    mark[coun++] = tree[x].color;	  ///要計算的區間包括了該區間
    return ;
  }

  LL tmp=x<<1;
  LL mid=(tree[x].l+tree[x].r)>>1;

  if(r<=mid)   return query(l,r,tmp);
  else if(l>mid)  return query(l,r,tmp+1);
  else{
        query(l,mid,tmp) ;
        query(mid+1,r,tmp+1);
  }
}

int main()
{
  int x1[MAX<<2],x2[MAX<<2],c[MAX<<2];
  int sum[MAX<<2];
  int mmax=-1,m;
  while(~scanf("%d",&m))
  {
    coun = 0;
    mmax=-1;
    memset(mark,-1,sizeof(mark));
    memset(sum,0,sizeof(sum));
    for(int i=0;i<m;i++)
    {
      scanf("%d%d%d",&x1[i],&x2[i],&c[i]);
      if(mmax<x1[i]) mmax = x1[i];
      if(mmax<x2[i]) mmax = x2[i];
    }
    build(1,mmax,1);
    for( int i = 0;i<m; i++ ){
      if(x1[i]+1>x2[i]) continue;
      update(x1[i]+1,x2[i],c[i],1);  ///這個地方需要加1,減1因為線段樹是從1開始的所以表示單位沒有0,因此不好處理
    }
    if(mmax<0) continue;
    query(1,mmax,1);
    for(int i=0;i<coun;){  ///注意統計的方式,下面的while不能少
        if(mark[i]==-1){i++ ;continue;}
        int x = mark[i];
        while(x == mark[++i] && i<coun);
        sum[x]++;
    }
    for(int i=0;i<8010;i++)
        if(sum[i])
         printf("%d %d\n",i,sum[i]);
    printf("\n");
  }
  return 0;
}

因為在更新時候和Build不一樣,所以程式碼風格很差,所以做一個整潔的。。。(小區別:見下面!!!)

但是之後再看好像下面的沒有上面的更加簡潔,因為上面的只要在函式開頭加一個返回條件就行了,但是下面的需要增加好幾個if。。。

#pragma comment(linker, "/STACK:102400000,102400000"
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define MAX 8010
#define INF 0x3f3f3f3f
#define LL long long
#define pii pair<int,int>
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
///map<int,int>mmap;
///map<int,int >::iterator it;
using namespace std;

struct Tree
{
  int  l,r;
  int color;
};
Tree tree[MAX*4];

void pushdown(LL x)  ///用於更新color陣列
{
  LL tmp = x<<1 ;
  tree[tmp].color =  tree[x].color;  ///由子節點通過增加
  tree[tmp+1].color = tree[x].color;
  tree[x].color=-1;
}
void build(int l,int r,int x)
{
  tree[x].l=l , tree[x].r=r , tree[x].color=-1;
  if(l==r)  return ;
  int tmp=x<<1;
  int mid=(l+r)>>1;
  build(l,mid,tmp);
  build(mid+1,r,tmp+1);
}

void update(int  l,int  r,int c,int x)  ///分別表示區間的左 , 右 , 增加的值  ,當前父親節點
{
  if(r<tree[x].l||l>tree[x].r)   return ;  ///!!!其實相較於上面一個程式碼,其實這裡沒必要返回了,因為已經在下面更改的if else else中判斷了,但是最好還是這麼寫
  if(l<=tree[x].l&&r>=tree[x].r)  ///該區間為需要更新區間的子區間
  {
    tree[x].color = c;
    return ;
  }
  if(tree[x].color!=-1)  pushdown(x);  ///更新從上向下更新color

  LL tmp=x<<1;
  LL mid=(tree[x].l+tree[x].r)>>1;

  if(r<=mid)   update(l,r,c,tmp);
  else if(l>mid) update(l,r,c,tmp+1);
  else{
        update(l,mid,c,tmp) ;
        update(mid+1,r,c,tmp+1);
  }
}

int mark[MAX<<2],coun=0; ///注意大小
void query(int  l ,int  r ,int x )
{
  if((l==tree[x].l&&r==tree[x].r && tree[x].color!=-1)  ||  tree[x].l == tree[x].r){
    mark[coun++] = tree[x].color;	  ///要計算的區間包括了該區間
    return ;
  }

  LL tmp=x<<1;
  LL mid=(tree[x].l+tree[x].r)>>1;

  if(r<=mid)   query(l,r,tmp);
  else if(l>mid)  query(l,r,tmp+1);
  else{
        query(l,mid,tmp) ;
        query(mid+1,r,tmp+1);
  }
}

int main()
{
  int x1[MAX<<2],x2[MAX<<2],c[MAX<<2];
  int sum[MAX<<2];
  int mmax=-1,m;
  while(~scanf("%d",&m))
  {
    coun = 0;
    mmax=-1;
    memset(mark,-1,sizeof(mark));
    memset(sum,0,sizeof(sum));
    for(int i=0;i<m;i++)
    {
      scanf("%d%d%d",&x1[i],&x2[i],&c[i]);
      if(mmax<x1[i]) mmax = x1[i];
      if(mmax<x2[i]) mmax = x2[i];
    }
    build(1,mmax,1);
    for( int i = 0;i<m; i++ ){
      if(x1[i]+1>x2[i]) continue;
      update(x1[i]+1,x2[i],c[i],1);  ///這個地方需要加1,減1也可以,如下
    }
    if(mmax<0) continue;
    query(1,mmax,1);
    for(int i=0;i<coun;){  ///注意統計的方式,下面的while不能少
        if(mark[i]==-1){i++ ;continue;}
        int x = mark[i];
        while(x == mark[++i] && i<coun);
        sum[x]++;
    }
    for(int i=0;i<8010;i++)
        if(sum[i])
         printf("%d %d\n",i,sum[i]);
    printf("\n");
  }
  return 0;
}

減1的,大同小異

//線段樹區間更新
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<iomanip>
#include<string>
#include<climits>
#include<cmath>
#define INF 0x3f3f3f3f
#define MAX 8010
#define LL long long
using namespace std;

struct Tree
{
  int  l,r;
  int color;
};
Tree tree[MAX*4];

void pushdown(LL x)  ///用於更新color陣列
{
  LL tmp = x<<1 ;
  tree[tmp].color =  tree[x].color;  ///由子節點通過增加
  tree[tmp+1].color = tree[x].color;
  tree[x].color=-1;
}
void build(int l,int r,int x)
{
  tree[x].l=l , tree[x].r=r , tree[x].color=-1;
  if(l==r)  return ;
  int tmp=x<<1;
  int mid=(l+r)>>1;
  build(l,mid,tmp);
  build(mid+1,r,tmp+1);
}

void update(int  l,int  r,int c,int x)  ///分別表示區間的左 , 右 , 增加的值  ,當前父親節點
{
  if(r<tree[x].l||l>tree[x].r)   return ;
  if(l<=tree[x].l&&r>=tree[x].r)  ///該區間為需要更新區間的子區間
  {
    tree[x].color = c;
    return ;
  }
  if(tree[x].color!=-1)  pushdown(x);  ///更新從上向下更新color
  update(l,r,c,x<<1);
  update(l,r,c,(x<<1)+1);
}

int mark[MAX<<2],coun=0; ///注意大小
void query(int  l ,int  r ,int x )
{
  if((l==tree[x].l&&r==tree[x].r && tree[x].color!=-1)  ||  tree[x].l == tree[x].r){
    mark[coun++] = tree[x].color;	  ///要計算的區間包括了該區間
    return ;
  }

  LL tmp=x<<1;
  LL mid=(tree[x].l+tree[x].r)>>1;

  if(r<=mid)   return query(l,r,tmp);
  else if(l>mid)  return query(l,r,tmp+1);
  else{
        query(l,mid,tmp) ;
        query(mid+1,r,tmp+1);
  }
}

int main()
{
  int x1[MAX<<2],x2[MAX<<2],c[MAX<<2];
  int sum[MAX<<2];
  int mmax=-1,m;
  while(~scanf("%d",&m))
  {
    coun = 0;
    mmax=-1;
    memset(mark,-1,sizeof(mark));
    memset(sum,0,sizeof(sum));
    for(int i=0;i<m;i++)
    {
      scanf("%d%d%d",&x1[i],&x2[i],&c[i]);
      if(mmax<x1[i]) mmax = x1[i];
      if(mmax<x2[i]) mmax = x2[i];
    }
    build(0,mmax-1,1);
    for( int i = 0;i<m; i++ ){
      if(x1[i]>x2[i]-1) continue;
      update(x1[i],x2[i]-1,c[i],1);  ///這個地方需要加1,減1因為線段樹是從1開始的所以表示單位沒有0,因此不好處理
    }
    if(mmax<0) continue;
    query(0,mmax-1,1);
    for(int i=0;i<coun;){  ///注意統計的方式,下面的while不能少
        if(mark[i]==-1){i++ ;continue;}
        int x = mark[i];
        while(x == mark[++i] && i<coun);
        sum[x]++;
    }
    for(int i=0;i<8010;i++)
        if(sum[i])
         printf("%d %d\n",i,sum[i]);
    printf("\n");
  }
  return 0;
}