poj2777(线段树)

题目链接:https://vjudge.net/problem/POJ-2777

题意:有L块连续的板子,每块板子最多染一种颜色,有T种(<=30)颜色,刚开始将所有板子染成颜色1,O次操作(包括将[a,b]染成颜色k,和询问[a,b]的不同颜色数),输出每次询问的值。

思路:典型的线段树的题目。用线段树实现表示一段区间的颜色值。线段树结点的属性包括l(区间左端点),r(区间右端点),value(区间的颜色值,1..T表示对应的颜色,0表示多种颜色),lazy(懒惰标记,如果不用lazy直接用value同时表示颜色值和懒惰标记会超时,因为将value作为懒惰标记时下放操作会将value置0,每次query几乎都要访问到每个叶子结点,复杂度为O(n),整个程序复杂度为(O^2))。每次query可以通过vis数组保存颜色i是否出现并由此记录不同颜色数,起始这里颜色数<=30就可以用二进制的为来表示颜色,不过用vis数组也不会超时,就懒得写进制方法了。

AC代码:

#include<cstdio>
using namespace std;
const int maxn=100005;

struct node{
    int l,r,value,lazy;
}tr[maxn<<2];

int L,T,O,vis[35],ans;
char c;

void build(int v,int l,int r){
    tr[v].l=l,tr[v].r=r;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(v<<1,l,mid);
    build(v<<1|1,mid+1,r);
}

void pushdown(int v){
    tr[v<<1].value=tr[v<<1].lazy=tr[v].lazy;
    tr[v<<1|1].value=tr[v<<1|1].lazy=tr[v].lazy;
    tr[v].lazy=0;
}

void update(int v,int l,int r,int k){
    if(l<=tr[v].l&&r>=tr[v].r){
        tr[v].value=tr[v].lazy=k;
        return;
    }
    if(tr[v].lazy) pushdown(v);
    int mid=(tr[v].l+tr[v].r)>>1;
    if(l<=mid) update(v<<1,l,r,k);
    if(r>mid) update(v<<1|1,l,r,k);
    if(tr[v<<1].value==tr[v<<1|1].value)
        tr[v].value=tr[v<<1].value;
    else
        tr[v].value=0;
}

void query(int v,int l,int r){
    if(l<=tr[v].l&&r>=tr[v].r&&tr[v].value){
        if(!vis[tr[v].value]){
            ++ans;
            vis[tr[v].value]=1;
        }
        return;
    }
    if(tr[v].lazy) pushdown(v);
    int mid=(tr[v].l+tr[v].r)>>1;
    if(l<=mid) query(v<<1,l,r);
    if(r>mid) query(v<<1|1,l,r);
    if(tr[v<<1].value==tr[v<<1|1].value)
        tr[v].value=tr[v<<1].value;
    else
        tr[v].value=0;
}

int main(){
    scanf("%d%d%d",&L,&T,&O);
    build(1,1,L);
    update(1,1,L,1);
    while(O--){
        scanf(" %c",&c);
        int a,b,k;
        if(c==‘C‘){
            scanf("%d%d%d",&a,&b,&k);
            if(a>b){
                int t=a;a=b,b=t;
            }
            update(1,a,b,k);
        }
        else{
            scanf("%d%d",&a,&b);
            if(a>b){
                int t=a;a=b,b=t;
            }
            ans=0;
            for(int i=1;i<=T;++i)
                vis[i]=0;
            query(1,a,b);
            printf("%d\n",ans);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/FrankChen831X/p/10786156.html

时间: 2024-10-24 00:30:15

poj2777(线段树)的相关文章

POJ2777 线段树区间染色问题

题意:给L长度的木板,给T种颜色,给O个操作,每次可以选择一段区间染色,或查询一个区间的颜色种类 思路:用线段树叶节点记录颜色所代表的数字,父节点为-1表示两个子节点颜色不相同,>0时的数字代表子节点全为这个数字对应颜色. #include <iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std

POJ2777(线段树涂色问题)

Count Color Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 42828   Accepted: 12973 Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem.

POJ2777线段树染色+lazy

第一次写这样的题目,反正出现了各种坑爹问题,调了老半天,除了延迟标记外,这题还要开一个cnt数组用来存储各个区间内颜色的种类数, 每次改变颜色时,更新一次. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #i

POJ2777 Count Color 线段树区间更新

题目描述: 长度为L个单位的画板,有T种不同的颜料,现要求按序做O个操作,操作分两种: 1."C A B C",即将A到B之间的区域涂上颜色C 2."P A B",查询[A,B]区域内出现的颜色种类 出现操作2时,请输出答案 PS:初始状态下画板颜色为1 一开始没有想那么好,用int整型位移来代替颜色,还是使用了最传统的bool color[来记录,可是不知道错在了哪里, #include<iostream> #include<cstdio>

poj 2528 Mayor&#39;s posters【离散化+线段树】

题目:poj 2528 Mayor's posters 题意:给一个长度非常长的墙上贴长度为ai的海报,由于有的会覆盖掉,求最后能看见的海报个数. 分析:题目和POJ2777 一模一样,方法也一样,只不过这个要离散化,其次要数组开大一点.至少2倍. 离散化的时候用了C++的 pair 类,还是比较好用的. 代码: #include <iostream> #include <algorithm> #include <utility> #include <cstrin

线段树&#183;题解报告

线段树·题解报告 参考资料 ·课件 线段树 --刘汝佳 统计的力量,线段树全接触 --张昆玮 ·Blog [完全版]线段树 从普通线段树到zkw线段树 [总结][数据结构]ZKW线段树详解 选题目录 · Hdu1166 敌兵布阵(单点更新,区间求和) · Hdu1754 I Hate It(单点更新,RMQ) · Hdu3308 LCIS(单点更新,区间并) · Poj3468 A Simple Problem with Integers(区间加减,区间求和) · Poj2777 Count C

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)

[BZOJ4942][Noi2017]整数 题目描述去uoj 题解:如果只有加法,那么直接暴力即可...(因为1的数量最多nlogn个) 先考虑加法,比较显然的做法就是将A二进制分解成log位,然后依次更新这log位,如果最高位依然有进位,那么找到最高位后面的第一个0,将中间的所有1变成0,那个0变成1.这个显然要用到线段树,但是复杂度是nlog2n的,肯定过不去. 于是我在考场上yy了一下,这log位是连续的,我们每次都要花费log的时间去修改一个岂不是很浪费?我们可以先在线段树上找到这段区间

bzoj1798: [Ahoi2009]Seq 维护序列seq 线段树

题目传送门 这道题就是线段树 先传乘法标记再传加法 #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M=400010; LL read(){ LL ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}