P2486 【SDOI2011】 染色
第二个合并时询问时答案的合并
以上处理了该区间完整作为答案的情况,但是会有这样的情况

一号节点内部经过第一个合并一定是合法的,但是答案是二号节点+一号节点,他们不是一个完整区间,需要递归到二号节点,假设二号节点右边界和一号节点左边界相等,这种情况我们没有处理,就炸了。
所以我们在线段树\(query\)函数时分三种情况考虑:\(1.\)答案区间都在左子树,则递归左子树\(\ \ 2.\)都在右子树,同理\(\ \ 3.\)答案跨越\(mid\),即在左子树和右子树,分别递归下去只可能处问题的是左儿子的右边界和右儿子的左边界(因为我们不能直接使用当前节点当答案所以第一个合并在这里无效),且答案区间一定包含这两个,记录这两个值,比较相同则\(ans--\)即可。
第三个合并是树链剖分多个区间时的合并(这也是最容易错的)
一条重链在线段树种是连续的,但一条链的这些区间在线段树中是不连续的,所以显然答案有可能出错,对于较深的那个点,用\(pos1\)记录这次线段树访问的左边界(就是在树链中靠上那个点)的颜色,访问下个线段树区间时拿这次线段树访问的左边界(在线段树询问中记录左右端点即可)和\(pos1\)比较如果相同则\(ans--\),再用左边界更新\(pos1\),较浅的那个点同理记录,如果交换两个点注意\(pos1,pos2\)也要交换。到最后的重链注意两个边界可能都要更新,都要判断
画张图就明白了

注意几点
\(1.\)树链剖分\(dfs1\)处理重儿子时尽量别用全局变量记录最大儿子的节点数,否则会剖错。
对于所有递归函数,使用全局变量一是可能被错误更新而不回溯,二是每一次每一层都要更新,更新次数太多常数大
综上尽量在递归中使用全局变量
\(2.\)对于线段树合并,不仅要考虑\(push_up\)的合并,也要考虑\(query\)时答案区间合并,还要考虑外部数据结构中线段树区间不连续时的合并,一定要考虑周全
\(3.\)对于线段树区间修改一般要使用懒标记!!!!注意\(tag\)标记别乱传,被清零再传就被\(0\)覆盖了,注意判断
细节看代码
\(Code\)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 200010
#define re register
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Edge{
int v,nxt;
}e[maxn<<2];
char cs;
int ans;
int id[maxn],top[maxn],head[maxn],a,b,c;
int cnt,cnts,x,y,fa[maxn],siz[maxn],maxson;
int wt[maxn],tag[maxn<<2],L,R;
int dep[maxn],n,m,mson[maxn],w[maxn];
inline void add(int u,int v)
{
e[++cnts].v=v;
e[cnts].nxt=head[u];
head[u]=cnts;
}
//---------------10.18线段树--------------------
#define ls p<<1
#define rs p<<1|1
struct T{
int l,r,cl,cr,mid,val;
}tre[maxn<<2];
void push_up(int p)//注意结构体中每一个可被更新的状态都要更新
{
tre[p].cl=tre[ls].cl;
tre[p].cr=tre[rs].cr;
tre[p].val=tre[ls].val+tre[rs].val;
if(tre[ls].cr==tre[rs].cl) tre[p].val--;//判断
}
void push_down(int p)
{
tre[ls].cl=tre[ls].cr=tre[rs].cl=tre[rs].cr=tag[p];
tre[ls].val=tre[rs].val=1;//贡献为0
tag[ls]=tag[rs]=tag[p];//区间覆盖取最新的懒标记直接覆盖前面的懒标记即可
tag[p]=0;//懒标记清零
}
void build(int l,int r,int p)
{
tre[p].l=l;
tre[p].r=r;
tre[p].mid=(l+r)>>1;
if(l==r)
{
tre[p].val=1;
tre[p].cl=tre[p].cr=wt[l];
return;
}
build(l,tre[p].mid,ls);
build(tre[p].mid+1,r,rs);
push_up(p);
}
void modify(int p,int ql,int qr,int z)
{
if(ql<=tre[p].l&&tre[p].r<=qr)
{
tag[p]=z;
tre[p].cl=tre[p].cr=z;
tre[p].val=1;
return;
}
if(tag[p]) push_down(p);//注意tag标记别乱传,被清零再传就被0覆盖了,注意判断
if(ql<=tre[p].mid) modify(ls,ql,qr,z);
if(qr>tre[p].mid) modify(rs,ql,qr,z);
push_up(p);
}
void query(int p,int ql,int qr)
{
if(ql<=tre[p].l&&tre[p].r<=qr)
{
ans+=tre[p].val;
if(tre[p].l==ql) L=tre[p].cl;//记录树链剖分中整个询问区间的左右端点颜色,方便第三个合并
if(tre[p].r==qr) R=tre[p].cr;
return;
}
if(tag[p]) push_down(p);//注意
if(qr<=tre[p].mid) query(ls,ql,qr);//分三种情况讨论,方便合并
else if(ql>tre[p].mid) query(rs,ql,qr);
else
{
query(ls,ql,qr);
query(rs,ql,qr);
if(tre[ls].cr==tre[rs].cl) ans--;//区间合并判断二
}
}
//----------------------------------------------
void dfs1(int u,int fat,int deep)
{
dep[u]=deep;
fa[u]=fat;
siz[u]=1;
int maxson=-1;//一定定义全局变量
//或者直接这么写 if(siz[ev]>siz[mson[u]]) mson[u]=ev;因为一开始都是0所以没错
for(re int i=head[u];i;i=e[i].nxt)
{
int ev=e[i].v;
if(ev==fat) continue;
dfs1(ev,u,deep+1);
siz[u]+=siz[ev];
if(siz[ev]>maxson)
{
maxson=siz[ev];
mson[u]=ev;
}
}
}
void dfs2(int u,int topf)
{
id[u]=++cnt;
top[u]=topf;
wt[cnt]=w[u];
if(!mson[u]) return;
dfs2(mson[u],topf);
for(int i=head[u];i;i=e[i].nxt)
{
int ev=e[i].v;
if(ev==fa[u]) continue;
if(ev==mson[u]) continue;
dfs2(ev,ev);
}
}
void add_Range(int u,int v,int k)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
modify(1,id[top[u]],id[u],k);
u=fa[top[u]];
}
if(id[u]<id[v]) swap(u,v);
modify(1,id[v],id[u],k);
}
int ask(int u,int v)
{
int res=0,pos1=0,pos2=0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v),swap(pos1,pos2);//pos1,pos2千万别忘记交换
ans=0;
query(1,id[top[u]],id[u]);
res+=ans;
if(R==pos1) res--; //右边界和上一次的左边界,第三个合并判断
pos1=L,u=fa[top[u]];//别忘了更新pos1
}
if(dep[u]<dep[v]) swap(u,v),swap(pos1,pos2);
ans=0;
query(1,id[v],id[u]);
res+=ans;
if(L==pos2) res--;
if(R==pos1) res--;//最后注意两个边界可能都要更新,都要判断
return res;
}
int main()
{
n=read(),m=read();
for(re int i=1;i<=n;++i) w[i]=read();
for(re int i=1;i<n;++i)
{
x=read(),y=read();
add(x,y);
add(y,x);
}
dfs1(1,0,1);
dfs2(1,1);
build(1,n,1);
for(re int i=1;i<=m;++i)
{
cin>>cs;
if(cs=='C')
{
a=read(),b=read(),c=read();
add_Range(a,b,c);
}
else if(cs=='Q')
{
a=read(),b=read();
printf("%d\n",ask(a,b));
}
}
return 0;
}

浙公网安备 33010602011771号