[SDOI2011]染色題解
阿新 • • 發佈:2021-07-31
[SDOI2011]染色題解
今天的bug實在改不出來,寫道之前做的題的題解
洛谷題目連結
這道題與noi2021的第一題輕重邊幾乎一模一樣,把這道題程式碼稍微修一下就可以過那道題
[noi2021輕重邊題目連結](https://www.luogu.com.cn/problem/P7735)
建議寫完這道題把如上題目也寫了
正文
很容易想到暴力解法,我們考慮優化,很明顯的區間修改所以使用線段樹或者樹狀陣列優化,但是需要在樹上操作所以我們需要使這個區間有序可以修改,所有需要使用樹鏈剖分使用剖分序從中建線段樹,注意,修改並不是直接修改而是找公共祖先的過程中修改有序的序列。
其實畫個圖很好理解但是畫圖太麻煩了,所以我不畫了
就此修改算是結束了。
接下來是查詢,我們同樣需要找最近公共祖先的過程中,查詢,接下來是本題這關鍵點。
查詢過程中我們需要合併的兩個序列其中左序列的右端點和右序列的左端點,設suml為左序列的顏色段數,sumr為有序列的顏色段數,l為左序列的右端點,r為右序列的左端點。
sum=suml+sumr;
if(l==r)sum--;
解釋:因為合併的兩個區間相交的位置為同一個顏色所以sum多加了1,因此需要減1.
原始碼如下
#include<bits/stdc++.h> using namespace std; const int MN=1e5+100; int n,m; struct tree{ int l,r,lc,rc,sum; }t[MN<<2]; int color[MN],head[MN],cnt; struct node{ int nxt,to; }e[MN<<1]; inline void add(int a,int b){ e[++cnt].nxt=head[a],head[a]=cnt,e[cnt].to=b; } int fa[MN],dep[MN],siz[MN],son[MN]; void dfs1(int now,int pre){ dep[now]=dep[pre]+1; fa[now]=pre; siz[now]=1; for(int i=head[now];i;i=e[i].nxt){ int to=e[i].to; if(to==pre)continue; dfs1(to,now); siz[now]+=siz[to]; if(siz[to]>siz[son[now]]){ son[now]=to; } } } int top[MN],dfn[MN],tot,name[MN]; void dfs2(int now,int pre){ top[now]=pre; dfn[now]=++tot; name[tot]=now; if(son[now]){ dfs2(son[now],pre); } for(int i=head[now];i;i=e[i].nxt){ int to=e[i].to; if(to==fa[now]||to==son[now])continue; dfs2(to,to); } } int tag[MN<<2]; inline void pushup(int id){ t[id].sum=t[id<<1].sum+t[id<<1|1].sum; if(t[id<<1].rc==t[id<<1|1].lc)t[id].sum--; t[id].lc=t[id<<1].lc,t[id].rc=t[id<<1|1].rc; } inline void pushdown(int id){ if(tag[id]==0)return; t[id<<1].sum=1,t[id<<1|1].sum=1,tag[id<<1]=tag[id],tag[id<<1|1]=tag[id]; t[id<<1].lc=t[id<<1].rc=tag[id],t[id<<1|1].lc=t[id<<1|1].rc=tag[id]; tag[id]=0; } void build(int id,int l,int r){ t[id].l=l,t[id].r=r; if(l==r){ t[id].lc=t[id].rc=color[name[l]]; t[id].sum=1; return; } int mid=(l+r)>>1; build(id<<1,l,mid),build(id<<1|1,mid+1,r); pushup(id); } void update(int id,int l,int r,int c){ if(t[id].l>=l&&t[id].r<=r){ t[id].sum=1; t[id].lc=c; t[id].rc=c; tag[id]=c; return; } int mid=(t[id].l+t[id].r)>>1; pushdown(id); if(l<=mid)update(id<<1,l,r,c); if(r>=mid+1)update(id<<1|1,l,r,c); pushup(id); } void change(int a,int b,int c){ while(top[a]!=top[b]){ if(dep[top[a]]<dep[top[b]])swap(a,b); update(1,dfn[top[a]],dfn[a],c); a=fa[top[a]]; } if(dep[a]<dep[b])swap(a,b); update(1,dfn[b],dfn[a],c); } int get_sum(int id,int l,int r){ if(t[id].l>=l&&t[id].r<=r){ return t[id].sum; } int mid=(t[id].l+t[id].r)>>1,ans=0; pushdown(id); if(l<=mid)ans+=get_sum(id<<1,l,r); if(r>=mid+1)ans+=get_sum(id<<1|1,l,r); if(l<=mid&&r>=mid+1)if(t[id<<1].rc==t[id<<1|1].lc)ans--; return ans; } int get_color(int id,int k){ if(t[id].l==t[id].r){ return t[id].lc; } int mid=(t[id].l+t[id].r)>>1; pushdown(id); if(k<=mid)return get_color(id<<1,k); else return get_color(id<<1|1,k); } int query(int a,int b){ int ans=0,nowr,nowl; while(top[a]!=top[b]){ if(dep[top[a]]<dep[top[b]])swap(a,b); ans+=get_sum(1,dfn[top[a]],dfn[a]); nowr=get_color(1,dfn[top[a]]); nowl=get_color(1,dfn[fa[top[a]]]); a=fa[top[a]]; if(nowr==nowl)ans--;//這個地方容易忽略,鏈頭與其父親顏色一樣時需要減1 } if(dep[a]<dep[b])swap(a,b); ans+=get_sum(1,dfn[b],dfn[a]); return ans?ans:1; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ scanf("%d",&color[i]); } for(int i=1;i<n;++i){ int a,b; scanf("%d%d",&a,&b); add(a,b),add(b,a); } dfs1(1,0); dfs2(1,1); build(1,1,n); while(m--){ int a,b,c; char x; scanf("%s%d%d",&x,&a,&b); if(x=='Q'){ printf("%d\n",query(a,b)); } else{ scanf("%d",&c); change(a,b,c); } } return 0; }