poj-2777(区间线段树,求种类数模板)

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

参考文章:https://blog.csdn.net/heucodesong/article/details/81038360

题目大意:给出T中颜色,可以给一段区域涂色,初始是全为1的颜色,然后有两种操作

(1)C x y z表示将区间x到y的颜色更改为z

(2)P x y 表示查询区间x到y的颜色种类。

题目看起来不符合区间和的条件,但是可以通过二进制转化一下。

初始化肯定都是颜色1,就表示只有一种颜色,然后每次更新颜色时,取这个数的a[x]=1<<(Item-1),a[x]中的1的位置就表示每个颜色的位置

然后pushup操作就改为:a[x]=a[x*2]|a[x*2+1],a[x]中的1的个数就是这个区间颜色的个数。对于Item为什么要-1,其实就是初始颜色时1,就相当于从第0位开始,与数组的0-n-1类似。

(易错:我当时在查询函数中一直出错,但我还没发现,主要是考虑ans1,ans2,ans的初始值是0,这里我已开始写成了1,就错了,

还有ans=ans1|ans2这一步不能少,不然编译会出错,总之,先求出结果再返回。)

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn = 1e5+;
int a[maxn*],b[maxn*];
void Init()
{
memset(a,,sizeof(a));
memset(b,,sizeof(b));
}
void pushup(int x)
{
a[x]=a[x*]|a[x*+];
}
void pushdown(int x)
{
if(b[x])
{
a[x*]=b[x];
a[x*+]=b[x];
b[x*]=b[x];
b[x*+]=b[x];
b[x]=;
}
}
void build(int x,int l,int r)
{
if(l==r)
{
a[x]=;return ;
}
int mid=(l+r)/;
build(x*,l,mid);
build(x*+,mid+,r);
pushup(x);
}
void update(int x,int l,int r,int A,int B,int Item)
{
if(A<=l&&r<=B)
{
a[x]=<<(Item-);
b[x]=<<(Item-);
return ;
}
int mid=(l+r)/;
pushdown(x);
if(A<=mid) update(x*,l,mid,A,B,Item);
if(B>mid) update(x*+,mid+,r,A,B,Item);
pushup(x);
}
int query(int x,int l,int r,int A,int B)
{
if(A<=l&&r<=B)
{
return a[x];
}
int mid=(l+r)/;
pushdown(x);
int ans1=,ans2=,ans=;
if(A<=mid) ans1=query(x*,l,mid,A,B);
if(B>mid) ans2=query(x*+,mid+,r,A,B);
ans=ans1|ans2;
return ans;
}
int main(void)
{
int n,m,t,i,x,y,z;
while(~scanf("%d%d%d",&n,&t,&m))
{
Init();
build(,,n);
char str[];
while(m--)
{
scanf("%s",str);
if(str[]=='C')
{
scanf("%d%d%d",&x,&y,&z);
if(x>y)
{
int tp=x;
x=y;
y=tp;
}
update(,,n,x,y,z);
}
else
{
scanf("%d%d",&x,&y);
if(x>y)
{
int tp=x;
x=y;
y=tp;
}
int cnt=,ans=query(,,n,x,y);
while(ans)
{
if(ans&) cnt++;
ans>>=;
}
printf("%d\n",cnt);
}
}
}
return ;
}
上一篇:TCP三次握手原理与SYN攻击


下一篇:用java实现文件下载,提示java.lang.IllegalStateException: getOutputStream() has already been called for this response