题目大意:
给定一个长度为n的序列,每一个位置的初始颜色都是1,现在有m个操作。
C l r x C l r x 将 [l,r] [ l , r ] 的颜色都修改成x
P l r P l r 查询[l,r]的颜色有多少种
其中!
颜色种类不超过30
经过观察,我们发现这个题的颜色种类特别少!那么我们可以直接将线段树节点所对应的区间含的颜色压成一个二进制
然后在合并的时候
f[root]=f[2∗root]|f[2∗root+1] f [ r o o t ] = f [ 2 ∗ r o o t ] | f [ 2 ∗ r o o t + 1 ]
就可以了
再就是query的时候,同样也是求一个总的按位或的一个值
直接求就行
而修改操作就是将 f[root] f [ r o o t ] 直接改为 (1<<p) ( 1 << p )
顺便下方标记就可以
上代码
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 1e5+1e2;
ll f[4*maxn],add[4*maxn];
int n,m;
int count(ll x)
{
int cnt=0;
while (x)
{
if (x&1) cnt++;
x>>=1;
}
return cnt;
}
void up(int root)
{
f[root]=(f[2*root]|f[2*root+1]);
}
void pushdown(int root,int l,int r)
{
if (add[root])
{
add[2*root]=add[root];
add[2*root+1]=add[root];
f[2*root]=(1 << add[root]);
f[2*root+1]=(1 << add[root]);
add[root]=0;
}
}
void build(int root,int l,int r)
{
if (l==r)
{
f[root]=2;
return;
}
int mid = (l+r) >> 1;
build(2*root,l,mid);
build(2*root+1,mid+1,r);
up(root);
}
void update(int root,int l,int r,int x,int y,ll p)
{
if (x<=l && r<=y)
{
f[root]=(1 << p);
add[root]=p;
return;
}
pushdown(root,l,r);
int mid = (l+r) >> 1;
if (x<=mid) update(2*root,l,mid,x,y,p);
if (y>mid) update(2*root+1,mid+1,r,x,y,p);
up(root);
}
ll query(int root,int l,int r,int x,int y)
{
if (x<=l && r<=y)
return f[root];
pushdown(root,l,r);
int mid = (l+r) >> 1;
ll ans=0;
if (x<=mid) ans=(ans | query(2*root,l,mid,x,y));
if (y>mid) ans=(ans | query(2*root+1,mid+1,r,x,y));
return ans;
}
int l;
int main()
{
scanf("%d%d%d",&n,&l,&m);
build(1,1,n);
for (int i=1;i<=m;i++)
{
char s[10];
scanf("%s",s+1);
if (s[1]=='C')
{
int x=read(),y=read();
if (x>y) swap(x,y);
ll z;
scanf("%lld",&z);
update(1,1,n,x,y,z);
}
if (s[1]=='P')
{
int x=read(),y=read();
if (x>y) swap(x,y);
//cout<1,1,n,x,y)<printf("%d\n",count(query(1,1,n,x,y)));
}
}
return 0;
}