【BZOJ】2243 [SDOI2011]染色 树链剖分

来源:互联网 发布:电影魔方软件 编辑:程序博客网 时间:2024/05/23 02:04

题目传送门

这题嘛,第一眼就是树链剖分的裸题,但是我只会用二进制记录颜色数量……看到这题的颜色范围就傻了……(呆.jpg)

因为这题112221定义为三段颜色,那么考虑两段线段合并,每段线段记录三个值:最左端颜色、最右端颜色、颜色段数。要得到总线端的颜色段数,只需考虑左边的线段的最右端颜色和右边的线段的最左端颜色是否相同即可。(说得好绕啊,但是其实不绕)

在询问的时候,还要考虑一下重链顶端和它的父亲的颜色是否相同。

那么这样线段树的处理就非常明朗了,但是这题有一个问题——在修改的时候也要push的。我在这里WA了好几发……(话说我是从什么时候开始养成修改时不push的坏习惯的?)

忍不住吐槽:这题的码量好大啊……好难调试啊……手都敲累了……

附上AC代码:

#include <cstdio>#include <cctype>#include <algorithm>using namespace std;const int N=1e5+10;struct tree{    int cl,cr,sum,lz;}t[N<<2];struct side{    int to,nt;}s[N<<1];int n,m,h[N],num,a[N];int d[N],f[N],sz[N],hs[N],top[N],wz[N],size,rl[N];inline char nc(void){    static char ch[100010],*p1=ch,*p2=ch;    return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;}inline void read(int &a){    static char c=nc();int f=1;    for (;!isdigit(c);c=nc()) if (c=='-') f=-1;    for (a=0;isdigit(c);a=(a<<3)+(a<<1)+c-'0',c=nc());    return (void)(a*=f);}inline void add(int x,int y){    s[++num]=(side){y,h[x]},h[x]=num;    s[++num]=(side){x,h[y]},h[y]=num;}inline void so1(int x,int fa){    d[x]=d[f[x]=fa]+1,sz[x]=1;    for (int i=h[x]; i; i=s[i].nt)        if (s[i].to!=fa){            so1(s[i].to,x);            sz[x]+=sz[s[i].to];            if (sz[s[i].to]>sz[hs[x]]) hs[x]=s[i].to;        }    return;}inline void so2(int x,int fa){    top[x]=fa,wz[x]=++size,rl[size]=x;    if (hs[x]) so2(hs[x],fa); else return;    for (int i=h[x]; i; i=s[i].nt)        if (s[i].to!=f[x]&&s[i].to!=hs[x]) so2(s[i].to,s[i].to);    return;}#define lt (k<<1)#define rt (k<<1|1)#define mid ((l+r)>>1)inline void updata(int k){    t[k].cl=t[lt].cl,t[k].cr=t[rt].cr;    t[k].sum=t[lt].sum+t[rt].sum-(t[lt].cr==t[rt].cl);}inline void push(int k){    t[lt].cl=t[lt].cr=t[lt].lz=t[k].lz,t[lt].sum=1;    t[rt].cl=t[rt].cr=t[rt].lz=t[k].lz,t[rt].sum=1;    return (void)(t[k].lz=-1);}inline void build(int k,int l,int r){    t[k].lz=-1;    if (l==r) return (void)(t[k].cl=t[k].cr=a[rl[l]],t[k].sum=1);    build(lt,l,mid),build(rt,mid+1,r);    return updata(k);}inline void change(int k,int l,int r,int ql,int qr,int w){    if (l>=ql&&r<=qr) return (void)(t[k].cl=t[k].cr=t[k].lz=w,t[k].sum=1);    if (t[k].lz!=-1) push(k);    if (ql<=mid) change(lt,l,mid,ql,qr,w);    if (qr>mid) change(rt,mid+1,r,ql,qr,w);    return updata(k);}inline void updata(int x,int y,int w){    for (int fx=top[x],fy=top[y]; fx!=fy; x=f[fx],fx=top[x]){        if (d[fx]<d[fy]) swap(x,y),swap(fx,fy);        change(1,1,n,wz[fx],wz[x],w);    }    if (d[x]>d[y]) swap(x,y);    return change(1,1,n,wz[x],wz[y],w);}inline tree query(int k,int l,int r,int ql,int qr){    if (l>=ql&&r<=qr) return t[k];    if (t[k].lz!=-1) push(k);    tree now={0},tmp={0};    bool bo=0;    if (ql<=mid) now=query(lt,l,mid,ql,qr),bo=1;    if (qr>mid)        if (!bo) now=query(rt,mid+1,r,ql,qr);        else{            tmp=query(rt,mid+1,r,ql,qr);            now.sum+=tmp.sum-(now.cr==tmp.cl),now.cr=tmp.cr;        }    return now;}inline int ask(int x,int y){    int ret=0,pre1=-1,pre2=-1;tree tmp;    for (int fx=top[x],fy=top[y]; fx!=fy; x=f[fx],fx=top[x]){        if (d[fx]<d[fy]) swap(x,y),swap(fx,fy),swap(pre1,pre2);        tmp=query(1,1,n,wz[fx],wz[x]),ret+=tmp.sum-(pre1==tmp.cr),pre1=tmp.cl;    }    if (d[x]>d[y]) swap(x,y),swap(pre1,pre2);    tmp=query(1,1,n,wz[x],wz[y]);    return ret+tmp.sum-(tmp.cl==pre1)-(tmp.cr==pre2);}int main(void){    read(n),read(m);    for (int i=1; i<=n; ++i) read(a[i]);    for (int i=1,x,y; i<n; ++i,read(x),read(y),add(x,y));    so1(1,0),so2(1,1),build(1,1,n);    while (m--){        char c=nc();while (c!='C'&&c!='Q') c=nc();        int x,y,w;read(x),read(y);        if (c=='C') read(w),updata(x,y,w);        else printf("%d\n",ask(x,y));    }    return 0;}
原创粉丝点击