【小结】树链剖分
树链剖分是把树上问题转化为区间问题的工具,额外耗时 \(O(log{n})\)。
树链剖分不一定要套线段树。
P2486 [SDOI2011] 染色
直接区间染色,区间查询颜色段。难点在于线段树部分和链的拼接。
首先来看线段树。发现是好合并的,把两边颜色数相加,如果左边的右端点等于右边的左端点,那么减一。
然后修改就是把一段覆盖,标记是好下传的。
对于链的拼接:每一次记录要跳的那边的下面最后一个。然后判断是否相等。最后判一下即可。
#include<bits/stdc++.h>
using namespace std;
/*
*/
struct FSI{
template<typename T>
FSI& operator >> (T &res){
res=0;T f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){res=res*10+ch-'0';ch=getchar();}
res*=f;
return *this;
}
}scan;
const int N=1e5+10;
int n,m,i,w[N];
int x,y,z,last[N],c;
int dep[N],son[N],sz[N];
int dfn[N],idx,id[N];
int top[N],v;
int f[N];
int lc,rc;
char op;
struct Edge{
int to,next;
}e[N<<1];
struct Node{
int l,r,sum,tag,L,R;
}a[N<<2];
void add(int u,int v)
{
e[++c]={v,last[u]};
last[u]=c;
}
void dfs(int x,int fa)
{
int i,to;
f[x]=fa;
sz[x]=1;
son[x]=0;
dep[x]=dep[fa]+1;
for (i=last[x];i;i=e[i].next)
{
to=e[i].to;
if (to==fa) continue;
dfs(to,x);
sz[x]+=sz[to];
if (sz[to]>sz[son[x]]) son[x]=to;
}
}
void fun(int x)
{
int i,to;
dfn[x]=++idx;
id[idx]=x;
if (son[x])
{
top[son[x]]=top[x];
fun(son[x]);
}
for (i=last[x];i;i=e[i].next)
{
to=e[i].to;
if (to==f[x]||to==son[x]) continue;
top[to]=to;
fun(to);
}
}
void pushup(int k)
{
a[k].L=a[k<<1].L;
a[k].R=a[k<<1|1].R;
if (a[k<<1].R==a[k<<1|1].L) a[k].sum=a[k<<1].sum+a[k<<1|1].sum-1;
else a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
}
void build(int k,int l,int r)
{
a[k].l=l;
a[k].r=r;
if (l==r)
{
a[k].sum=1;
a[k].L=a[k].R=w[id[l]];
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void change(int k,int v)
{
a[k].L=a[k].R=v;
a[k].sum=1;
a[k].tag=v;
}
void pushdown(int k)
{
if (a[k].tag)
{
change(k<<1,a[k].tag);
change(k<<1|1,a[k].tag);
a[k].tag=0;
}
}
void update(int k,int x,int y,int v)
{
int l=a[k].l,r=a[k].r;
if (l>=x&&r<=y)
{
change(k,v);
return;
}
pushdown(k);
int mid=l+r>>1;
if (x<=mid) update(k<<1,x,y,v);
if (y>mid) update(k<<1|1,x,y,v);
pushup(k);
}
int query(int k,int x,int y)
{
int l=a[k].l,r=a[k].r;
if (l>=x&&r<=y)
{
if (l==x) lc=a[k].L;
if (r==y) rc=a[k].R;
return a[k].sum;
}
pushdown(k);
int mid=l+r>>1,res=0;
if (x<=mid) res+=query(k<<1,x,y);
if (y>mid) res+=query(k<<1|1,x,y);
if (x<=mid&&y>mid&&a[k<<1].R==a[k<<1|1].L) res--;
return res;
}
void update_path(int x,int y,int v)
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,dfn[top[x]],dfn[x],v);
x=f[top[x]];
}
if (dep[x]<dep[y]) swap(x,y);
update(1,dfn[y],dfn[x],v);
}
int query_path(int x,int y)
{
int res=0,cx=0,cy=0;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y),swap(cx,cy);
res+=query(1,dfn[top[x]],dfn[x]);
if (rc==cx) res--;
cx=lc;
x=f[top[x]];
}
if (dep[x]<dep[y]) swap(x,y),swap(cx,cy);
res+=query(1,dfn[y],dfn[x]);
if (rc==cx) res--;
if (lc==cy) res--;
return res;
}
int main()
{
scan>>n>>m;
for (i=1;i<=n;i++) scan>>w[i];
for (i=1;i<n;i++)
{
scan>>x>>y;
add(x,y);
add(y,x);
}
dfs(1,0);
top[1]=1;
fun(1);
build(1,1,n);
while (m--)
{
scanf("%s",&op);
scan>>x>>y;
if (op=='C') scan>>z,update_path(x,y,z);
else printf("%d\n",query_path(x,y));
}
return 0;
}
P7735 [NOI2021] 轻重边
和上一题有异曲同工之妙。
有一个难想的转化:每次做 1 操作相当于把路径上的点全部标为一个与其他点不相同的颜色,查询时查询相邻颜色相同点对。
然后就是上一题了。
注意线段树每个节点都要打 \(tag=-1\)。
#include<bits/stdc++.h>
using namespace std;
/*
1
8 1
2 1
3 2
4 1
5 2
6 3
7 3
8 2
2 7 1
线段树初始 tag=-1 一定要赋值给全局,而不是 l=r。
*/
struct FSI{
template<typename T>
FSI& operator >> (T &res){
res=0;T f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){res=res*10+ch-'0';ch=getchar();}
res*=f;
return *this;
}
}scan;
const int N=1e5+10;
int T,n,m,i,x,y,op,col;
int last[N],c;
int sz[N],son[N],f[N],dep[N];
int top[N],dfn[N],id[N],idx;
int lc,rc;
struct Edge{
int to,next;
}e[N<<1];
struct Node{
int l,r,sum,L,R,tag;
}a[N<<2];
void add(int u,int v)
{
e[++c]={v,last[u]};
last[u]=c;
}
void dfs(int x,int fa)
{
int i,to;
sz[x]=1;
dep[x]=dep[fa]+1;
f[x]=fa;
for (i=last[x];i;i=e[i].next)
{
to=e[i].to;
if (to==fa) continue;
dfs(to,x);
sz[x]+=sz[to];
if (sz[to]>sz[son[x]]) son[x]=to;
}
}
void fun(int x)
{
int i,to;
dfn[x]=++idx;
id[idx]=x;
if (son[x])
{
top[son[x]]=top[x];
fun(son[x]);
}
for (i=last[x];i;i=e[i].next)
{
to=e[i].to;
if (to==f[x]||to==son[x]) continue;
top[to]=to;
fun(to);
}
}
void pushup(int k)
{
a[k].L=a[k<<1].L;
a[k].R=a[k<<1|1].R;
if (a[k<<1].R==a[k<<1|1].L) a[k].sum=a[k<<1].sum+a[k<<1|1].sum+1;
else a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
}
void change(int k,int v)
{
a[k].L=a[k].R=v;
a[k].sum=a[k].r-a[k].l;
a[k].tag=v;
}
void pushdown(int k)
{
if (a[k].tag!=-1)
{
change(k<<1,a[k].tag);
change(k<<1|1,a[k].tag);
a[k].tag=-1;
}
}
void build(int k,int l,int r)
{
a[k].l=l;
a[k].r=r;
a[k].tag=-1;//线段树初始 tag=-1 一定要赋值给全局,而不是 l=r。
if (l==r)
{
a[k].sum=0;
a[k].L=a[k].R=++col;
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void update(int k,int x,int y,int v)
{
int l=a[k].l,r=a[k].r;
if (l>=x&&r<=y)
{
change(k,v);
return;
}
pushdown(k);
int mid=l+r>>1;
if (x<=mid) update(k<<1,x,y,v);
if (y>mid) update(k<<1|1,x,y,v);
pushup(k);
}
int query(int k,int x,int y)
{
int l=a[k].l,r=a[k].r;
if (l>=x&&r<=y)
{
if (l==x) lc=a[k].L;
if (r==y) rc=a[k].R;
return a[k].sum;
}
pushdown(k);
int mid=l+r>>1,res=0;
if (x<=mid) res+=query(k<<1,x,y);
if (y>mid) res+=query(k<<1|1,x,y);
if (x<=mid&&y>mid&&a[k<<1].R==a[k<<1|1].L) res++;
return res;
}
void update_path(int x,int y,int v)
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,dfn[top[x]],dfn[x],v);
x=f[top[x]];
}
if (dep[x]<dep[y]) swap(x,y);
update(1,dfn[y],dfn[x],v);
}
int query_path(int x,int y)
{
int res=0,tx=-1,ty=-1,t;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y),swap(tx,ty);
t=query(1,dfn[top[x]],dfn[x]);
if (tx==rc) t++;
res+=t;
tx=lc;
x=f[top[x]];
}
if (dep[x]<dep[y]) swap(x,y),swap(tx,ty);
t=query(1,dfn[y],dfn[x]);
if (lc==ty) t++;
if (rc==tx) t++;
res+=t;
return res;
}
void work()
{
scan>>n>>m;
for (i=1;i<=n;i++) last[i]=son[i]=0;
col=0;
c=0;
idx=0;
for (i=1;i<n;i++)
{
scan>>x>>y;
add(x,y);
add(y,x);
}
dfs(1,0);
top[1]=1;
fun(1);
build(1,1,n);
while (m--)
{
scan>>op>>x>>y;
if (op==1) update_path(x,y,++col);
else printf("%d\n",query_path(x,y));
}
}
int main()
{
scan>>T;
while (T--) work();
return 0;
}

浙公网安备 33010602011771号