题目链接:https://vjudge.net/problem/POJ-2777
题意:有L块连续的板子,每块板子最多染一种颜色,有T种(<=30)颜色,刚开始将所有板子染成颜色1,O次操作(包括将[a,b]染成颜色k,和询问[a,b]的不同颜色数),输出每次询问的值。
思路:典型的线段树的题目。用线段树实现表示一段区间的颜色值。线段树结点的属性包括l(区间左端点),r(区间右端点),value(区间的颜色值,1..T表示对应的颜色,0表示多种颜色),lazy(懒惰标记,如果不用lazy直接用value同时表示颜色值和懒惰标记会超时,因为将value作为懒惰标记时下放操作会将value置0,每次query几乎都要访问到每个叶子结点,复杂度为O(n),整个程序复杂度为(O^2))。每次query可以通过vis数组保存颜色i是否出现并由此记录不同颜色数,起始这里颜色数<=30就可以用二进制的为来表示颜色,不过用vis数组也不会超时,就懒得写进制方法了。
AC代码:
#include<cstdio> using namespace std; const int maxn=100005; struct node{ int l,r,value,lazy; }tr[maxn<<2]; int L,T,O,vis[35],ans; char c; void build(int v,int l,int r){ tr[v].l=l,tr[v].r=r; if(l==r) return; int mid=(l+r)>>1; build(v<<1,l,mid); build(v<<1|1,mid+1,r); } void pushdown(int v){ tr[v<<1].value=tr[v<<1].lazy=tr[v].lazy; tr[v<<1|1].value=tr[v<<1|1].lazy=tr[v].lazy; tr[v].lazy=0; } void update(int v,int l,int r,int k){ if(l<=tr[v].l&&r>=tr[v].r){ tr[v].value=tr[v].lazy=k; return; } if(tr[v].lazy) pushdown(v); int mid=(tr[v].l+tr[v].r)>>1; if(l<=mid) update(v<<1,l,r,k); if(r>mid) update(v<<1|1,l,r,k); if(tr[v<<1].value==tr[v<<1|1].value) tr[v].value=tr[v<<1].value; else tr[v].value=0; } void query(int v,int l,int r){ if(l<=tr[v].l&&r>=tr[v].r&&tr[v].value){ if(!vis[tr[v].value]){ ++ans; vis[tr[v].value]=1; } return; } if(tr[v].lazy) pushdown(v); int mid=(tr[v].l+tr[v].r)>>1; if(l<=mid) query(v<<1,l,r); if(r>mid) query(v<<1|1,l,r); if(tr[v<<1].value==tr[v<<1|1].value) tr[v].value=tr[v<<1].value; else tr[v].value=0; } int main(){ scanf("%d%d%d",&L,&T,&O); build(1,1,L); update(1,1,L,1); while(O--){ scanf(" %c",&c); int a,b,k; if(c=='C'){ scanf("%d%d%d",&a,&b,&k); if(a>b){ int t=a;a=b,b=t; } update(1,a,b,k); } else{ scanf("%d%d",&a,&b); if(a>b){ int t=a;a=b,b=t; } ans=0; for(int i=1;i<=T;++i) vis[i]=0; query(1,a,b); printf("%d\n",ans); } } return 0; }