给定一个长度为 n n n的序列,每个点有初始的权值 v i v_i vi和标记值 z l i zl_i zli,有 m m m次操作
破裂的规则:在该区间内【后面位置判断你的是 z l zl zl】,如果存在一个位置 l l l是0,下一个位置 r = l + 1 r=l+1 r=l+1是1,则 l , r l,r l,r破裂,接着令 l = l − 1 , r = r + 1 l=l-1,r=r+1 l=l−1,r=r+1继续判断,直到不合法为止
数据范围: n ≤ 1 0 5 n\leq 10^5 n≤105
方法1
方法2(接下来讲解的做法)
细节很多,这里用样例来解释
定义三个变量 n o w , r o o t , n now,root,n now,root,n,分别表示现在所处的节点,根节点,树的大小
上述过程均需要建边
n=1;root=1;now=1;
for(register int i=1;i<=nn;i++)
if(zl[i]==0) //指令是0
{
pos[i]=++n;//新建节点,第i跳边对应现在n这个点
add(now,n);add(n,now);//建边
val[n].first=v[i];//保存权值,first表示这个点的入边(从上面下来的边)
fa[n]=now;now=n;//保存父亲节点,并往下跳跃
}
else//否则回跳
{
if(now==root)//已经是根了
{
pos[i]=now;//保存第i跳边对应now这个点
val[now].second=v[i];
root=++n;//新的根
add(now,n);add(n,now);
now=root;
}
else
{
pos[i]=now;
val[now].second=v[i];
now=fa[now];//回退
}
}
但是树链剖分+线段树是只能求点值的,所以我们把每条边的值都存到深度较深的那个点上,用一个二元组存(代码中有体现),变成下面这张图(第一维表示从上面下来的,第二维维表示上去的,若没有则为0)
对于二元组的每一维,分别开线段树维护即可,注意在交换 x , y x,y x,y时,需要改变方向
时间复杂度: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
#include
#include
#include
#include
#define N 100010
#define LL long long
using namespace std;int nn,n,m,root,fa[N],l[N],tot,x,y,dep[N],siz[N],son[N],id[N],cnt,top[N],opt,v[N],now,pos[N];
pair<int,int>nval[N],val[N];
bool zl[N];
inline LL read()
{
char c;LL d=1,f=0;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
struct node{int next,to;}e[N<<1];
inline void add(int u,int v){e[++tot]=(node){l[u],v};l[u]=tot;return;}
inline void dfs1(int x)//以下是树链剖分部分
{
siz[x]=1;
int maxn=0;
for(register int i=l[x];i;i=e[i].next)
{
int y=e[i].to;
if(y==fa[x]) continue;
dep[y]=dep[x]+1;fa[y]=x;
dfs1(y);
siz[x]+=siz[y];
if(siz[y]>maxn) maxn=siz[son[x]=y];
}
return;
}
inline void dfs2(int x,int topf)
{
id[x]=++cnt;
top[x]=topf;
nval[cnt]=val[x];
if(son[x]==0) return;
dfs2(son[x],topf);
for(register int i=l[x];i;i=e[i].next)
{
int y=e[i].to;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
return;
}
struct xds//线段树维护部分
{
#define lson k<<1
#define rson k<<1|1
int maxn[N<<3];
inline void pushup(int k)
{
maxn[k]=max(maxn[lson],maxn[rson]);
return;
}
inline void build(bool flg,int k=1,int l=1,int r=cnt)
{
if(l==r)
{
if(flg==0) maxn[k]=nval[l].first;
else maxn[k]=nval[l].second;//记得上行和下行分别维护
return;
}
int mid=l+r>>1;
build(flg,lson,l,mid);build(flg,rson,mid+1,r);
pushup(k);
return;
}
inline void Modify(bool flg,int x,int v,int k=1,int l=1,int r=cnt)
{
if(l==r)
{
if(flg==0)nval[l].first=v;
else nval[l].second=v;//分别赋值
maxn[k]=v;return;
}
int mid=l+r>>1;
if(x<=mid) Modify(flg,x,v,lson,l,mid);
if(x>mid) Modify(flg,x,v,rson,mid+1,r);
pushup(k);
return;
}
inline int Ask(int ql,int qr,int k=1,int l=1,int r=cnt)
{
if(ql>qr) return 0;
if(ql<=l&&r<=qr) return maxn[k];
int res=0;int mid=l+r>>1;
if(ql<=mid) res=max(res,Ask(ql,qr,lson,l,mid));
if(qr>mid) res=max(res,Ask(ql,qr,rson,mid+1,r));
pushup(k);
return res;
}
#undef lson
#undef rson
}T1,T2;
inline int Query(bool flg,int x,int y)
{
int res=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y),flg^=1;//变向时要改变方向
if(flg==0) res=max(T1.Ask(id[top[x]],id[x]),res);
else res=max(T2.Ask(id[top[x]],id[x]),res);//上行和下行分别计算
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
else flg^=1;
if(id[x]==id[y]) return res;//同一个点无需再算
if(flg==0) res=max(T1.Ask(id[son[x]],id[y]),res);//注意是son[x],因为它们的LCA是不做贡献的
else res=max(T2.Ask(id[son[x]],id[y]),res);
return res;
}
signed main()
{
int size = 256 << 20; //250M
char*p=(char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p) );
nn=read();m=read();
for(register int i=1;i<=nn;i++) zl[i]=read();
for(register int i=1;i<=nn;i++) v[i]=read();
n=1;root=1;now=1;
for(register int i=1;i<=nn;i++)
if(zl[i]==0) //指令是0
{
pos[i]=++n;//新建节点,第i跳边对应现在n这个点
add(now,n);add(n,now);//建边
val[n].first=v[i];//保存权值,first表示这个点的入边(从上面下来的边)
fa[n]=now;now=n;//保存父亲节点,并往下跳跃
}
else//否则回跳
{
if(now==root)//已经是根了
{
pos[i]=now;//保存第i跳边对应now这个点
val[now].second=v[i];
root=++n;//新的根
add(now,n);add(n,now);
now=root;
}
else
{
pos[i]=now;
val[now].second=v[i];
now=fa[now];//回退
}
}
dfs1(root);
dfs2(root,root);
T1.build(0);T2.build(1);
while(m--)
{
opt=read();
if(opt==1)
{
x=read();y=read();
v[x]=y;//顺便修改
if(zl[x]==0) T1.Modify(0,id[pos[x]],y);//单点修改
else T2.Modify(1,id[pos[x]],y);
}
if(opt==2)
{
x=read();y=read();
if(x==y)
{
printf("%d\n",v[x]);//直接输出
continue;
}
int px=pos[x],py=pos[y];//用树上的点
if(px==py&&zl[x]!=zl[y])//两边相反
{
printf("0\n");
continue;
}
if(zl[x]==0) px=fa[px];//这个点没有贡献,跳到它的父亲
if(zl[y]==1) py=fa[py];//同理
printf("%d\n",Query(1,px,py));//查询
}
}
}