https://www.luogu.org/problemnew/show/P2486git
看上去又是一道強行把序列上問題搬運到樹上的裸題,然而分析以後發現並否則...數組
首先咱們考慮如何在序列上維護信息:從最簡單的想起,若是兩個相鄰的元素合併,顯然是這兩個元素所含顏色段個數(其實就是1)加起來,若是兩個元素顏色相同就減1;那麼兩個分別含有兩個元素的相鄰區間合併,仍是把這兩個區間所含顏色段個數加起來,若是左區間最右邊的顏色等於右區間最左邊的顏色就減去1.ui
如此咱們已經獲得線段樹維護信息的方法,記錄區間所含顏色段個數,區間最左邊顏色及區間最右邊顏色,固然爲了\(pushdown\)咱們還得維護一個\(tag\)數組表示覆蓋標記,而後按上面方法就行了spa
可是在樹鏈剖分查詢兩點之間時就與序列上不一樣了.有一個問題,就是當前鏈最左邊的顏色若是和上面那條鏈最右邊的顏色相等的話,須要將貢獻減1.有一個\(naiive\)的方法是每次查詢鏈時再查一下上面那條鏈最右邊的顏色(其實就是單點查詢\(fa[top[x]]\)的顏色),而後這個方法看起來不優美,其實有個更妙的方法code
咱們每次查完一條鏈後記錄該鏈最左邊的顏色,同時將該鏈最右邊的顏色與上一次記錄的值比較。看起來很容易但有個問題就是你多是從\(LCA\)兩個不一樣的子樹上向LCA跳,而後從 @ qscqesze_lca 的題解中學到了一個小trick輕易解決了這個問題,請看代碼get
#include <cstdio> #include <cstring> #include <algorithm> #include <cctype> #include <cstdlib> #include <vector> #define ll long long #define ri register int #define ull unsigned long long using std::min; using std::max; using std::swap; template <class T>inline void read(T &x){ x=0;int ne=0;char c; while(!isdigit(c=getchar()))ne=c=='-'; x=c-48; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48; x=ne?-x:x;return ; } const int maxn=100005; const int inf=0x7fffffff; int n,m; struct Edge{ int ne,to; }edge[maxn<<1]; int h[maxn],num_edge=1; inline void add_edge(int f,int to){ edge[++num_edge].ne=h[f]; edge[num_edge].to=to; h[f]=num_edge; } int col[maxn]; int dep[maxn],fa[maxn],size[maxn],dfn[maxn],rnk[maxn],tot=0,top[maxn],son[maxn]; void dfs_1(int now){ int v;size[now]=1; for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; if(v==fa[now])continue; dep[v]=dep[now]+1,fa[v]=now; dfs_1(v); size[now]+=size[v]; if(!son[now]||size[v]>size[son[now]])son[now]=v; } return ; } void dfs_2(int now,int t){ int v;top[now]=t,dfn[now]=++tot,rnk[tot]=now; if(!son[now])return ; dfs_2(son[now],t); for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; if(v==fa[now]||v==son[now])continue; dfs_2(v,v); } return ; } int num[maxn<<2],lc[maxn<<2],rc[maxn<<2],tag[maxn<<2]; int L,R,dta; void build(int now,int l,int r){ tag[now]=-1; if(l==r){ num[now]=1; lc[now]=rc[now]=col[rnk[l]]; return ; } int mid=(l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0); lc[now]=lc[now<<1],rc[now]=rc[now<<1|1]; return ; } inline void pushdown(int now){ if(tag[now]!=-1){ tag[now<<1]=tag[now<<1|1]=tag[now]; lc[now<<1]=lc[now<<1|1]=lc[now]; rc[now<<1]=rc[now<<1|1]=rc[now]; num[now<<1]=num[now<<1|1]=1; tag[now]=-1; } return ; } void update(int now,int l,int r){ if(L<=l&&r<=R){ num[now]=1; lc[now]=rc[now]=dta; tag[now]=dta; return ; } int mid=(l+r)>>1; pushdown(now); if(L<=mid)update(now<<1,l,mid); if(mid<R)update(now<<1|1,mid+1,r); num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0); lc[now]=lc[now<<1],rc[now]=rc[now<<1|1]; return ; } int chain_lc,chain_rc; int query(int now,int l,int r){ if(L==l)chain_lc=lc[now]; if(R==r)chain_rc=rc[now]; if(L<=l&&r<=R){ return num[now]; } int ans=0,mid=(l+r)>>1; pushdown(now); if(L<=mid&&mid<R)ans=query(now<<1,l,mid)+query(now<<1|1,mid+1,r)-(rc[now<<1]==lc[now<<1|1]?1:0); else if(L<=mid)ans=query(now<<1,l,mid); else if(mid<R)ans=query(now<<1|1,mid+1,r); //num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0); //lc[now]=lc[now<<1],rc[now]=rc[now<<1|1]; return ans; } inline void update_path(int x,int y,int c){ dta=c; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); L=dfn[top[x]],R=dfn[x]; update(1,1,n); x=fa[top[x]]; } if(dfn[x]>dfn[y])swap(x,y); L=dfn[x],R=dfn[y]; update(1,1,n); return ; } int lst_1,lst_2;//lst_1老是你當前正在查詢的鏈的上一條鏈的最左邊顏色 inline void query_path(int x,int y){ int ans=0; lst_1=lst_2=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) {swap(x,y),swap(lst_1,lst_2);}//很是高明的一個trick,不用刻意查詢鏈top父親的顏色 L=dfn[top[x]],R=dfn[x]; ans+=query(1,1,n); if(lst_1==chain_rc)ans--; lst_1=chain_lc; x=fa[top[x]]; } if(dfn[x]<dfn[y]) {swap(x,y),swap(lst_1,lst_2);} L=dfn[y],R=dfn[x]; ans+=query(1,1,n); if(chain_rc==lst_1)ans--; if(chain_lc==lst_2)ans--; printf("%d\n",ans); return ; } int main(){ int x,y,z; read(n),read(m); for(ri i=1;i<=n;i++)read(col[i]); for(ri i=1;i<n;i++){ read(x),read(y); add_edge(x,y); add_edge(y,x); } dep[1]=1,fa[1]=0; dfs_1(1); dfs_2(1,1); build(1,1,n); char opt[5]; while(m--){ scanf("%s",opt); if(opt[0]=='C'){ read(x),read(y),read(z); update_path(x,y,z); } else{ read(x),read(y); query_path(x,y); } } return 0; }