题解 P1558 【色板游戏】

小菜鸟

2019-09-29 19:10:20

Solution

~~已经忘记线段树的平衡树选手报到~~ 此题颜色数极少,显然可以对每种颜色暴力维护 但是写好几颗树很麻烦,我们就可以考虑把颜色压位 然后就得出区间合并的方法,`sum=lc->sum|rc->sum` 为了减少需要加一减一的边界细节,用平衡树来实现区间操作 ~~fhqtxdy!~~ 一定要注意,区间可能$l>r$,需要判断 真心感觉fhq好写,代码量跟线段树差不多甚至少于线段树 而且维护不同的信息只需要修改`pushdown`和`maintain`就行了 ~~常数也可以接受~~ ```cpp #include<cstdio> #include<cstdlib> #include<algorithm> struct Node { int val,sum,size,tag,pri; Node *lc,*rc; Node(int v): val(v), sum(v), size(1), tag(0), pri(rand()), lc(NULL), rc(NULL) {} void push_down() { if(tag==0)return; val=tag; sum=tag; if(lc!=NULL)lc->tag=tag; if(rc!=NULL)rc->tag=tag; tag=0; } void maintain() { size=1; sum=val; if(lc!=NULL) { lc->push_down(); size+=lc->size; sum|=lc->sum; } if(rc!=NULL) { rc->push_down(); size+=rc->size; sum|=rc->sum; } } }; Node *root; Node *merge(Node *l,Node *r) { if(l==NULL)return r; if(r==NULL)return l; if(l->pri<r->pri) { l->push_down(); l->rc=merge(l->rc,r); l->maintain(); return l; } else { r->push_down(); r->lc=merge(l,r->lc); r->maintain(); return r; } } void split(Node *rt,int k,Node *&l,Node *&r) { if(rt==NULL) { l=r=NULL; return; } int s=0; if(rt->lc!=NULL)s=rt->lc->size; rt->push_down(); if(s+1<k) { l=rt; split(l->rc,k-s-1,l->rc,r); } else { r=rt; split(r->lc,k,l,r->lc); } rt->maintain(); } void paint(int l,int r,int c) { Node *p1,*p2,*p3; split(root,r+1,p2,p3); split(p2,l,p1,p2); p2->tag=1<<c; root=merge(merge(p1,p2),p3); } int bit_count(int x) { int res=0; while(x) { res+=x&1; x>>=1; } return res; } int query(int l,int r) { Node *p1,*p2,*p3; split(root,r+1,p2,p3); split(p2,l,p1,p2); p2->push_down(); int res=bit_count(p2->sum); root=merge(merge(p1,p2),p3); return res; } int n,m; char readc() { char c=getchar(); while(c<'A'||c>'Z')c=getchar(); return c; } int main() { scanf("%d%*d%d\n",&n,&m); for(int i=0;i<n;++i) { root=merge(root,new Node(2)); } while(m--) { char op; int l,r,c; op=readc(); scanf("%d%d",&l,&r); if(l>r)std::swap(l,r); if(op=='C') { scanf("%d",&c); paint(l,r,c); } if(op=='P') { printf("%d\n",query(l,r)); } } } ```