题目链接: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线段树专题写完了,已经捞了七八十道字符串题目准备操刀。(图论去死,数论去死)