题目链接:统计颜色
cf上面好像做过一样的题 但是那个太难找了 为啥要贴呢 因为有时候总想不到这个状压(主要是tcl)
其实一般能不能状压 都有提示 比如数据范围在 63以内啊 这时候就可以用 longlong 来状压了
我们发现维护 区间有几种颜色很难 (有更新操作) 但是观察颜色最多才60种 那我们拿一个ll来代表 颜色 如果第i种颜色有的话 那这个ll值的第i位为1 那一个区间的颜色种数 就是 把这些ll值都 或(位运算或操作)起来 更新的话就把对应的区间 都进行或操作就行 具体看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int n,m;
ll tree[N<<2],lazy[N<<2];
void pushdown(int id){
if(lazy[id]){
lazy[id<<1]|=lazy[id];
lazy[id<<1|1]|=lazy[id];
tree[id<<1]|=lazy[id];
tree[id<<1|1]|=lazy[id];
lazy[id]=0;
}
}
void pushup(int id){
tree[id]=tree[id<<1]|tree[id<<1|1];
}
void update(int id,int l,int r,int L,int R,ll val){
if(L<=l&&R>=r){
tree[id]|=val;
lazy[id]|=val;
return;
}
int mid = l+r>>1;
pushdown(id);
if(L<=mid) update(id<<1,l,mid,L,R,val);
if(R>mid) update(id<<1|1,mid+1,r,L,R,val);
pushup(id);
}
ll query(int id,int l,int r,int L,int R){
if(L<=l&&R>=r){
return tree[id];
}
int mid = l+r>>1;
ll ans = 0;
pushdown(id);
if(L<=mid) ans|=query(id<<1,l,mid,L,R);
if(R>mid) ans|=query(id<<1|1,mid+1,r,L,R);
return ans;
}
int cxt(ll val){
int cnt = 0;
while(val){
cnt++;
val-=val&-val;
}
return cnt;
}
int main(){
while(~scanf("%d%d",&n,&m)){
memset(tree,0,sizeof(tree));
memset(lazy,0,sizeof(lazy));
for(int i = 1; i <= m; i++){
int op,l,r,c;
scanf("%d%d%d",&op,&l,&r);
if(op==1){
scanf("%d",&c);
update(1,1,n,l,r,1ll<<(ll)c);
}else{
printf("%d\n",cxt(query(1,1,n,l,r)));
}
}
}
return 0;
}
如果你还想了解这种思想 可以看看:CodeForces - 1114F 线段树+欧拉函数+状压