POJ 2777 Count Color (线段树+(状压))

题目链接:http://poj.org/problem?id=2777

解题思路:


题目大意

操作1区间修改[A,B]改为C

操作2区间统计颜色[A,B]

注意给出的A,B不保证前者小,后者大


方法一:

统计颜色的时候刚开始直接没有任何优化扔进set发现TLE。

想想也是,对于1212121212121这种颜色区间一次询问的复杂度就要O(N*logN)

于是观察了一下颜色只有三十种,那么初始化也就非常省时间,同时统计已经记录的颜色到达T时,直接return就行了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define ll long long
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define mid int m=l+r>>1
#define tl tree[rt<<1]
#define tr tree[rt<<1|1]

using namespace std;

const int N = 1e5+5;

int tree[N<<2];
bool vis[31];
int t,cnt;

void push_up(int rt)
{
    tree[rt] = (tl==tr)? tl:-1;
}

void push_down(int rt)
{
    if (tree[rt] > 0) tl = tr = tree[rt];
}

void update(int L,int R,int col,int rt,int l,int r)
{
    if (L<=l && r<=R){
        tree[rt] = col;
        return ;
    }
    push_down(rt);
    mid;
    if (L<=m) update(L,R,col,lson);
    if (R>m ) update(L,R,col,rson);
    push_up(rt);
}

void output(int L,int R,int rt,int l,int r)
{
    if (cnt==t) return ;
    if (L<=l && r<=R && tree[rt]>0){
        //printf("[%d,%d] ->%d\n",l,r,tree[rt]);
        if (!vis[tree[rt]]){
            cnt++;
            vis[tree[rt]] = true;
        }
        return ;
    }
    mid;
    push_down(rt);
    if (L<=m) output(L,R,lson);
    if (R> m) output(L,R,rson);
}

int main()
{
    int n,q;
    while (~scanf("%d %d %d",&n,&t,&q)){
        update(1,n,1,1,1,n);
        char op[2];
        int a,b,c;
        while (q--){
            scanf("%s %d %d",op,&a,&b);
            if (a>b) swap(a,b);
            if (op[0]=='C'){
                scanf("%d",&c);
                update(a,b,c,1,1,n);
            }
            else {
                cnt = 0;
                memset(vis,false,sizeof vis);
                output(a,b,1,1,n);
                printf("%d\n",cnt);
            }
        }
    }
    return 0;
}

方法二

网上看了一下可以状压,每种颜色二进制占一位。

线段树维护的相当于一个二进制串。

当二进制串只有1位是1时,下放线段树。(也就是push_down,和上一份代码其实一个意思)

push_up就变成了 tree[rt] = tree[rt<<1] | tree[rt<<1|1]

询问就变成了区间合并得出[A,B]所维护的二进制串

不知道是不是像上一份代码当所有颜色都被统计到时直接return会快很多,不想加了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define ll long long
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define mid int m=l+r>>1
#define tl tree[rt<<1]
#define tr tree[rt<<1|1]

using namespace std;

const int N = 1e5+5;

int tree[N<<2];
int bina[]={1,2,4,8,16, 32,64,128,256,512, 1024,2048,4096,8192,(1<<14), (1<<15),(1<<16),(1<<17),(1<<18),(1<<19),
(1<<20),(1<<21),(1<<22),(1<<23),(1<<24), (1<<25),(1<<26),(1<<27),(1<<28),(1<<29)};
int ans;

void push_up(int rt)
{
    tree[rt] = tl|tr;
}

void push_down(int rt)
{
    if (binary_search(bina,bina+30,tree[rt])) tl = tr = tree[rt];
}

void update(int L,int R,int col,int rt,int l,int r)
{
    if (L<=l && r<=R){
        tree[rt] = (1<<(col-1));
        return ;
    }
    push_down(rt);
    mid;
    if (L<=m) update(L,R,col,lson);
    if (R>m ) update(L,R,col,rson);
    push_up(rt);
}

void output(int L,int R,int rt,int l,int r)
{
    if (L<=l && r<=R){
        ans = (ans|tree[rt]);
        return ;
    }
    mid;
    push_down(rt);
    if (L<=m) output(L,R,lson);
    if (R> m) output(L,R,rson);
}

int main()
{
    int n,t,q;
    while (~scanf("%d %d %d",&n,&t,&q)){
        update(1,n,1,1,1,n);
        char op[2];
        int a,b,c;
        while (q--){
            scanf("%s %d %d",op,&a,&b);
            if (a>b) swap(a,b);
            if (op[0]=='C'){
                scanf("%d",&c);
                update(a,b,c,1,1,n);
            }
            else {
                ans = 0;
                output(a,b,1,1,n);
                int cnt = 0;
                while (ans){
                    if (ans%2==1) cnt++;
                    ans >>= 1;
                }
                printf("%d\n",cnt);
            }
        }
    }
    return 0;
}

下面的字雨女无瓜


笨比の总结:

1.写第一份代码询问的时候直接忘记判断【L,R】,心里问问你寄几L,R都当参数了为什么不写。

2.承接错误1,询问部分没有push_down会发生的错误是,当前区间只有部分在询问中,而上一次恰好修改了整个区间,那么

到这里不push_down就垮了。

3.没看清题意导致第一遍写错题了,先按照思路把测试数据过一遍看看是不是你理解的意思。

4.o天呐终于把VJ 上一个kuangbin线段树专题写完了,已经捞了七八十道字符串题目准备操刀。(图论去死,数论去死)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值