#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int max_n = 1e5+5;
struct node{
int l,r;
int lc,rc,sum,delta;
}tree[max_n*4];
int fa[max_n],deep[max_n],nxt[max_n*2],point[max_n*2],v[max_n*2],val[max_n],top[max_n],size[max_n],rank_n[max_n];
char c;
int n,m,tot,x,y,k,last,cha;
inline void clear()
{
memset(point,-1,sizeof(point));
memset(nxt,-1,sizeof(nxt));
tot=0;
}
inline void addedge(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
inline void update(int now)
{
tree[now].sum=tree[now<<1].sum+tree[(now<<1)+1].sum;
tree[now].lc=tree[now<<1].lc;
tree[now].rc=tree[(now<<1)+1].rc;
if(tree[now<<1].rc==tree[(now<<1)+1].lc)
tree[now].sum-=1;
}
inline void pushdown(int now)
{
if(tree[now].delta!=0)
{
tree[now<<1].sum=1;
tree[now<<1].delta=tree[now].delta;
tree[now<<1].lc=tree[now<<1].rc=tree[now].delta;
tree[(now<<1)+1].sum=1;
tree[(now<<1)+1].delta=tree[now].delta;
tree[(now<<1)+1].lc=tree[(now<<1)+1].rc=tree[now].delta;
tree[now].delta=0;
}
}
inline void build(int now,int l,int r)
{
tree[now].l=l;
tree[now].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(now<<1,l,mid);
build((now<<1)+1,mid+1,r);
}
inline void change(int now,int l,int r,int val)
{
int l1=tree[now].l;
int r1=tree[now].r;
if(l1>=l && r1<=r)
{
tree[now].sum=1;
tree[now].delta=val;
tree[now].lc=tree[now].rc=val;
return;
}
pushdown(now);
int mid=(l1+r1)>>1;
if(l<=mid)
change(now<<1,l,r,val);
if(r>mid)
change((now<<1)+1,l,r,val);
update(now);
}
inline void schange(int x,int y,int val)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]]) swap(x,y);
change(1,rank_n[top[x]],rank_n[x],val);
x=fa[top[x]];
}
if(rank_n[x]>rank_n[y]) swap(x,y);
change(1,rank_n[x],rank_n[y],val);
}
inline void dfs1(int now,int f)
{
size[now]=1;
deep[now]=deep[f]+1;
fa[now]=f;
for(int i=point[now]; i!=-1; i=nxt[i])
if(v[i]!=f)
{
dfs1(v[i],now);
size[now]+=size[v[i]];
}
}
inline void dfs2(int now,int tip)
{
top[now]=tip;
rank_n[now]=++tot;
change(1,tot,tot,val[now]);
if(now!=1 && nxt[point[now]]==-1)
return;
int mson=0;
for(int i=point[now]; i!=-1; i=nxt[i])
if(size[v[i]]<size[now] && size[mson]<size[v[i]]) mson=v[i];
dfs2(mson,tip);
for(int i=point[now]; i!=-1; i=nxt[i])
if(size[v[i]]<size[now] && v[i]!=mson)
dfs2(v[i],v[i]);
}
inline int query(int now,int l,int r)
{
int l1=tree[now].l;int r1=tree[now].r;
if(l1>=l && r1<=r) return tree[now].sum;
pushdown(now);
int ans=0,mid=(l1+r1)>>1;
if(l<=mid)
ans+=query(now<<1,l,r);
if(r>mid)
ans+=query((now<<1)+1,l,r);
if(l<=mid && r>mid)
if(tree[now<<1].rc==tree[(now<<1)+1].lc) ans--;//判斷相鄰兩個色段交匯處有無同樣的顏色
return ans;
}
inline int query_c(int now,int tar)//查詢顏色
{
int l=tree[now].l;
int r=tree[now].r;
if(l==r && l==tar) return tree[now].lc;
pushdown(now);
int mid=(l+r)/2;
if(tar<=mid)
return query_c(now<<1,tar);
else
return query_c((now<<1)+1,tar);
}
inline int squery(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]]) swap(x,y);
ans+=query(1,rank_n[top[x]],rank_n[x]);
if(query_c(1,rank_n[fa[top[x]]])==query_c(1,rank_n[top[x]])) ans-=1;//和線段樹不同點:換重鏈是判斷相鄰重鏈是否顏色一致
x=fa[top[x]];
}
if(rank_n[x]>rank_n[y]) swap(x,y);
ans+=query(1,rank_n[x],rank_n[y]);
return ans;
}
int main()
{
clear();
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i)
scanf("%d",&val[i]);
for(int i=1; i<=n-1; ++i)
{
scanf("%d%d",&x,&y);
addedge(x,y);
}
tot=0;
build(1,1,n);
dfs1(1,0);
dfs2(1,1);
for(int i=1; i<=m; ++i)
{
cin>>c;
if(c=='Q')
{
scanf("%d%d",&x,&y);
printf("%d\n",squery(x,y));
}
else
{
scanf("%d%d%d",&x,&y,&k);
schange(x,y,k);
}
}
return 0;
}
[SDOI2011]染色
[SDOI2011]染色
describes
給定一棵有n個節點的無根樹和m個操作,操作有2類:
1、將節點a到節點b路徑上所有點都染成顏色c;
2、詢問節點a到節點b路徑上的顏色段數量(連續相同顏色被認爲是同一段),如“112221”由3段組成:“11”、“222”和“1”。
請你寫一個程序依次完成這m個操作。
Input
第一行包含2個整數n和m,分別表示節點數和操作數;
第二行包含n個正整數表示n個節點的初始顏色
下面 行每行包含兩個整數x和y,表示x和y之間有一條無向邊。
下面 行每行描述一個操作:
“C a b c”表示這是一個染色操作,把節點a到節點b路徑上所有點(包括a和b)都染成顏色c;
“Q a b”表示這是一個詢問操作,詢問節點a到節點b(包括a和b)路徑上的顏色段數量。
Output
對於每個詢問操作,輸出一行答案。
Sample Input
6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5
Sample Output
3
1
2
HINT
Time Limit: 20 Sec Memory Limit: 512 MB
數N<=10^5,操作數M<=10^5,所有的顏色C爲整數且在[0, 10^9]之間。
題解:
題目初看很容易理解,就知道是線段樹經典題目和樹鏈剖分的結合,用線段樹維護區間的sum,left color,right color,delta即可。update,和pushdown和線段樹裏的經典題一樣不用說了。
但是很容易發現的問題就是由於線段樹維護的是鏈剖之後的樹,也就是說查詢的區間可能不會相連。
這時可以發現可以通過原樹中的top[x],和fa[top[x]]將查詢的鏈聯繫起來。因爲如果換重鏈,相鄰的兩個節點一定是top[x]和fa[top[x]],接下來就想在線段樹上維護顏色一樣維護就行了。
即:
代碼:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.