链接:https://ac.nowcoder.com/acm/problem/15667
来源:牛客网
题目描述
n个桶按顺序排列,我们用1~n给桶标号。有两种操作:
1 l r c 区间[l,r]中的每个桶中都放入一个颜色为c的球 (1≤l,r ≤n,l≤r,0≤c≤60)
2 l r 查询区间[l,r]的桶中有多少种不同颜色的球 (1≤l,r ≤n,l≤r)
输入描述:
有多组数据,对于每组数据:
第一行有两个整数n,m(1≤n,m≤100000)
接下来m行,代表m个操作,格式如题目所示。
输出描述:
对于每个2号操作,输出一个整数,表示查询的结果。
思路:首先统计的是区间颜色个数,待修改,可以使用待修莫队。
这里我们可以观察下颜色数,题目给出了c在0-60,也就是说颜色最多60个
这里需要一个技巧,就是二进制,我们可以把颜色转成二进制,什么意思呢?
比如颜色是0,我们记做 2的0次方 ;颜色是1,我们记做2的1次方
那么最大也就2的60次方。我们用long long存就好了。
那么我们维护线段树的时候,区间修改,因为相同颜色只算一种嘛,我们就把线段树加的操作改成或运算 我们只记录1的贡献。
细节要注意longlong
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100000+100;
int n,m;
int op,l,r,c;
struct Segment_tree
{
int l,r;
ll sum;
ll lazy;
}T[N<<2];
void pushup(int p)
{
T[p].sum=T[p<<1].sum|T[p<<1|1].sum;
}
void pushdown(int p)
{
if(T[p].lazy)
{
T[p<<1].sum|=T[p].lazy;
T[p<<1|1].sum|=T[p].lazy;
T[p<<1].lazy|=T[p].lazy;
T[p<<1|1].lazy|=T[p].lazy;
T[p].lazy=0;
}
}
void build(int l,int r,int p)
{
T[p].l=l,T[p].r=r,T[p].lazy=0,T[p].sum=0;
if(l==r)
{
return ;
}
ll mid=(l+r)>>1;
build(l,mid,p<<1),build(mid+1,r,p<<1|1);
pushup(p);
}
void update(int l,int r,int val,int p)
{
if(l<=T[p].l&&T[p].r<=r)
{
T[p].sum|=(1ll<<val);//这里需要1ll,防止爆int
T[p].lazy|=(1ll<<val);
return ;
}
int mid=(T[p].l+T[p].r)>>1;
pushdown(p);
if(l<=mid)update(l,r,val,p<<1);
if(r>mid)update(l,r,val,p<<1|1);
pushup(p);
}
ll query(int l,int r,int p)
{
if(l<=T[p].l&&T[p].r<=r)
{
return T[p].sum;
}
int mid=(T[p].l+T[p].r)>>1;
pushdown(p);
ll ans=0;
if(l<=mid)ans|=query(l,r,p<<1);
if(r>mid)ans|=query(l,r,p<<1|1);
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
build(1,n,1);
while(m--)
{
scanf("%d%d%d",&op,&l,&r);
if(op==2)
{
ll res=query(l,r,1);
int ans=0;
//统计这个数1存在的个数,即为颜色数
while(res>0)
{
if(res&1)//和1并操作,看第一位是不是1
{
ans++;
}
res>>=1;//每次右移一位
}
printf("%d\n",ans);
}
else
{
scanf("%d",&c);
update(l,r,c,1);//区间更新
}
}
}
return 0;
}