题意:区间涂色,询问区间内颜色相同的个数
思路:将原区间划分乘sqrt(n)个区间,每次暴力查询和跟新两边的区间,中间的区间直接用hash存每种颜色的节点的数量。这里用到了类似线段树的lazy思想,区间成段修改直接打个标记,等到要划分这个区间的时候先把标记传下去,然后更新
代码:
#include <stdio.h> #include <math.h> #include <string.h> #include <map> #define MAXN 100005 int n,m,bsize,bnum,x[MAXN]; struct hash_block{ int cls,size; std::map<int,int> mp; }b[350]; void pushdown(int id){ hash_block &hb=b[id]; if(hb.cls!=-1){ for(int i=id*bsize;i<id*bsize+hb.size;i++)x[i]=hb.cls; hb.mp.clear(),hb.mp[hb.cls]=hb.size; hb.cls=-1; } } void update(int l,int r,int c){ int lb=l/bsize,rb=r/bsize,ans=0; for(int i=lb+1;i<rb;i++)b[i].cls=c; if(lb!=rb){ pushdown(lb);pushdown(rb); for(int i=l;i<lb*bsize+b[lb].size;i++) b[lb].mp[x[i]]--,b[lb].mp[c]++,x[i]=c; for(int i=rb*bsize;i<=r;i++) b[rb].mp[x[i]]--,b[rb].mp[c]++,x[i]=c; }else{ pushdown(lb); for(int i=l;i<=r;i++) b[lb].mp[x[i]]--,b[lb].mp[c]++,x[i]=c; } } int query(int l,int r,int c){ int lb=l/bsize,rb=r/bsize,ans=0; for(int i=lb+1;i<rb;i++){ if(b[i].cls==c)ans+=b[i].size; else if(b[i].cls==-1&&b[i].mp.find(c)!=b[i].mp.end())ans+=b[i].mp[c]; } if(lb!=rb){ pushdown(lb);pushdown(rb); for(int i=l;i<lb*bsize+b[lb].size;i++)ans+=(x[i]==c); for(int i=rb*bsize;i<=r;i++)ans+=(x[i]==c); }else{ pushdown(lb); for(int i=l;i<=r;i++)ans+=(x[i]==c); } return ans; } void initblock(){ bsize=(int)sqrt(n+1e-8); bnum=(n-1)/bsize+1; for(int i=0;i<bnum;i++){ b[i].mp.clear(); b[i].cls=-1; b[i].size=std::min(i*bsize+bsize,n)-i*bsize; } for(int i=0;i<n;i++){ scanf("%d",&x[i]); b[i/bsize].mp[x[i]]++; } } int q,l,r,z; int main(){ while(scanf("%d%d",&n,&m)!=EOF){ initblock(); while(m--){ scanf("%d%d%d%d",&q,&l,&r,&z); if(q==1)update(l,r,z); else printf("%d\n",query(l,r,z)); } } return 0; }
时间: 2024-10-09 21:39:40