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)
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号操作,输出一个整数,表示查询的结果。
示例1
输入
10 10 1 1 2 0 1 3 4 1 2 1 4 1 5 6 2 2 1 6 1 7 8 1 2 3 8 1 8 10 3 2 1 10 2 3 8
输出
2 3 2 4 3
思路:刚看到这道题就是想到了线段树,但是,难道要 在结构体中开一个60的数组吗,答案是否定,这样不是超时,就是 内存超限,但是是就没有不能就线段树写了吗? 当然不是,这时候我想到了状态压缩,不是有 60 中颜色吗, long long 有64,每一位代表当前颜色是不是不是存在; 还有就是 在用 左移 右移 运算符时 1<<val, 一定要强制转化为 long long ((ll)1<<val)
因为 << 运算符默认的是 int 型;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 100010
#define ll long long
struct node
{
ll x;
ll sum;
int downnode; // 延迟标记,(也可以吧x当做延迟标记,往下传时那就得把x清0)
// 但是不把x当做延迟标记的话,那么就把x清不清0,就不影响了,因为是二进制符号|;
} stu[Max*4];
int n,m;
void down(int root)
{
stu[2*root].x |= stu[root].x;
stu[2*root+1].x |= stu[root].x;
stu[2*root].sum |= stu[root].x;
stu[2*root+1].sum |= stu[root].x;
stu[2*root].downnode = 1;
stu[2*root+1].downnode = 1;
stu[root].downnode = 0;
}
void build(int root,int star,int end)
{
stu[root].x = 0;
stu[root].sum = 0;
stu[root].downnode = 0;
if(star==end)
return ;
int mid = (star+end)/2;
build(2*root,star,mid);
build(2*root+1,mid+1,end);
}
void updet(int root,int star,int end,int l,int r,int val)
{
if(star>r||end<l)
return ;
if(star>=l&&r>=end)
{
stu[root].downnode = 1;
stu[root].x |= (ll)1<<val; // 一定要强制转化为 long long
stu[root].sum |= (ll)1<<val;
return ;
}
if(stu[root].downnode)
{
down(root);
}
int mid = (star + end)/2;
if(l<=mid)
updet(root*2,star,mid,l,r,val);
if(r>mid)
updet(root*2+1,mid+1,end,l,r,val);
stu[root].sum = stu[root*2].sum | stu[root*2+1].sum;
}
ll query(int root,int star,int end,int l,int r)
{
if(star>r||end<l)
return 0;
if(star>=l&&end<=r)
return stu[root].sum;
if(stu[root].downnode)
down(root);
int mid = (star+end)/2;
ll t1 = 0,t2 = 0;
if(l<=mid)
t1 = query(root*2,star,mid,l,r);
if(r>mid)
t2 = query(root*2+1,mid+1,end,l,r);
return t1|t2;
}
int main()
{
int i,j,k;
while(~scanf("%d%d",&n,&m))
{
build(1,1,n);
//memset(stu,0,sizeof(stu));
int t;
for(i = 0;i<m;i++)
{
scanf("%d",&t);
int l,r,val;
if(t==1)
{
scanf("%d%d%d",&l,&r,&val);
updet(1,1,n,l,r,val);
}
else if(t==2)
{
scanf("%d%d",&l,&r);
ll t = query(1,1,n,l,r);
int sum = 0;
while(t)
{
if(t%2)
sum++;
t=t/2;
}
printf("%d\n",sum);
}
}
}
return 0;
}
方法二:
其实这道题有简单的方法,定义一个 结构体里面是区间 和 vecor,里面 v[i] 中存的是 颜色改变为 i 的 区间;每查询一次区间,就遍历这60种颜色,看看中间有没相交的部分,有的话,就把这种颜色算上,没有就不算;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 100010
#include<vector>
struct node
{
int l,r;
};
int n,m;
vector<node> v[61];
int main()
{
int i,j;
while(~scanf("%d%d",&n,&m))
{
for(i = 0;i<=60;i++)
v[i].clear();
int k;
int l,r,val;
for(i = 0;i<m;i++)
{
scanf("%d",&k);
if(k==1)
{
scanf("%d%d%d",&l,&r,&val);
node tt;
tt.l = l;
tt.r = r;
v[val].push_back(tt);
}
else if(k==2)
{
scanf("%d%d",&l,&r);
int sum =0,t;
for(t = 0;t<=60;t++)
{
for(j = 0;j<v[t].size();j ++)
{
node tt = v[t][j];
if(tt.r<l||tt.l>r)
continue;
else
{
sum++;
break;
}
}
}
printf("%d\n",sum);
}
}
}
return 0;
}