题目大意:给一个分成n段的木板涂色,后序涂色可能会覆盖之前的涂色,问最终露出来的颜色和他们的段数
输入:(可以有很多case)
n(1 <= n <= 8000)
第i次涂色的左端点 右端点 所涂的颜色序号(共n次涂色各占一行,所有数据的范围都是 [0, 8000])
输出:(每个case的输出中间有一个空行)
最终漏出的颜色序号 涂了它的段数(按照颜色序号从小到大输出)
分析:线段树涂色问题。与poj2528类似,但是2528是点着色(一个数已经代表一小段了),这个是段着色(一个数就是一个端点)。要注意一点,不能用n来建树,而是用8000,也就是说其实树是固定的。
更新时区间左值加1则与2528类似可以看成点来染色了,例如0 4 4就相当于染色1,2,3,4这四个段所以update(1,4,4)。update和down的部分都一样,与2528不同的是这个代码中的query将区间都压到了叶节点再进行统计,但是像2528那样做也可以(遇到单色区间就统计)。
代码:转载自http://blog.csdn.net/sin_xf/article/details/47726363
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #include<vector>
- #include<map>
- #include<set>
- #define INF 0x3f3f3f3f
- using namespace std;
- typedef long long ll;
- const int maxn = 1e4+5;
- int col[maxn<<2];
- int num[maxn];
- int ncol;
- void down(int l,int r,int rt){
- if(col[rt]!=-1){
- col[rt<<1]=col[rt<<1|1]=col[rt];
- col[rt]=-1;
- }
- }
- void update(int L,int R,int k,int l,int r,int rt){
- if(L<=l&&r<=R){
- col[rt]=k;
- return;
- }
- down(l,r,rt);
- int mid=(l+r)>>1;
- if(L<=mid) update(L,R,k,l,mid,rt<<1);
- if(R>mid) update(L,R,k,mid+1,r,rt<<1|1);
- }
- void query(int l,int r,int rt){
- if(l==r){
- if(col[rt]>=0&&col[rt]!=ncol) num[col[rt]]++; //统计连续颜色段的个数
- ncol=col[rt];//两个相邻区间颜色相同则不用统计成新的段数
- return;
- }
- down(l,r,rt); //将所有区间不是单色的都下压到叶子结点
- int mid=(l+r)>>1;
- query(l,mid,rt<<1);
- query(mid+1,r,rt<<1|1);
- }
- int main()
- {
- int n;
- while(scanf("%d",&n)!=-1){
- ncol=-1;
- memset(num,0,sizeof(num));
- memset(col,-1,sizeof(col)); //建树
- for(int i=1;i<=n;i++){
- int c,l,r;
- scanf("%d%d%d",&l,&r,&c);
- if(l<r) update(l+1,r,c,1,8000,1);
- }
- query(1,8000,1);
- for(int i=0;i<=8000;i++){
- if(num[i]) printf("%d %d\n",i,num[i]);
- }
- printf("\n");
- }
- }