您的位置:首页 > 其它

[BZOJ3589]动态树(树链剖分+dfs序+lca)

2016-09-24 07:34 274 查看

题目描述

传送门

题解

修改的操作直接做就可以了。查询的时候问题主要出在会有重复的路径。我的思路就是将重复的路径砍断,变成不重复的,然后最后依次查询就可以了。由于k比较小,O(k2)判断就是可以接受的。

由于树枝全部是从某个节点到根的路径的一段,处理起来就比较方便了。处理两条树枝的方法是:设两条树枝(x1,y1)(x2,y2),且h[x1]<h[y1],h[x2]<h[y2],h[x1]<h[x2]。首先求出r=lca(y1,y2),yy(为r的一个儿子,且满足y2在yy的子数中)。若h[x1]>h[r]或者h[x2]>h[r],则说明两条树枝不相交,可以不作任何处理。否则的话,使y1=yy。注意判断空树枝的情况。

这种方法只会把一条树枝砍得更短,而不会变长,所以不用担心和原先判断过的冲突。k2枚举一下就行了。

说一些写的时候出现的问题:

①做lca的时候某些时候需要将xy交换,但是交换了之后求出的两个儿子就对应不起来了。所以为了方便起见,按照y的深度排了一下序。

②这道题让我更深入地了解了lca。其实lca刚开始的时候一直向上蹦,它的原理是任意一个数都可以用二进制分解为至多logn个数相加,并且这样的话保证了x最后蹦到和y相同的高度。但是如果这样直接记录儿子的话是不行的,因为有可能最后蹦的不是一步。解决方法是加一个限制条件,y在蹦的过程中不能直接蹦到x的高度,但是最后要一步一步蹦上去。注意最后一定要让y蹦到和x相同的高度,否则后面的处理会有问题。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 200005
#define sz 18
#define LL long long

const LL Mod=2147483648LL;
int n,q,x,y,opt,dfs_clock,pt,add,k;
int tot,point
,nxt[N*2],v[N*2];
int father
,size
,son
,h
,top
,in
,out
,f
[sz+5],mi[sz+5];
LL sum[N*4],delta[N*4],ans;
struct hp{int x,y;}edge[10];
struct hq{int fa,xx,yy;};

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;
}
void dfs_1(int x,int fa,int dep)
{
size[x]=1; h[x]=dep; father[x]=fa;
for (int i=1;i<sz;++i)
{
if (h[x]-mi[i]<1) break;
f[x][i]=f[f[x][i-1]][i-1];
}
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
f[v[i]][0]=x;
dfs_1(v[i],x,dep+1);
size[x]+=size[v[i]];
if (size[son[x]]<size[v[i]]) son[x]=v[i];
}
}
void dfs_2(int x,int fa)
{
if (son[fa]!=x) top[x]=x;
else top[x]=top[fa];
in[x]=++dfs_clock;
if (son[x]) dfs_2(son[x],x);
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa&&v[i]!=son[x])
dfs_2(v[i],x);
out[x]=dfs_clock;
}
hq lca(int x,int y)
{
hq ans;
int xx=x,yy=y;
for (int i=sz-1;i>=0;--i)
while (h[f[x][i]]>=h[y]&&f[x][i]!=y)
xx=x,x=f[x][i];
while (h[f[x][0]]>=h[y]) xx=x,x=f[x][0];
if (x==y)
return ans=(hq){x,xx,yy};
for (int i=sz-1;i>=0;--i)
if (f[x][i]!=f[y][i])
yy=y,xx=x,x=f[x][i],y=f[y][i];
return ans=(hq){f[x][0],x,y};
}

void update(int now)
{
sum[now]=(sum[now<<1]+sum[now<<1|1])%Mod;
}
void pushdown(int now,int l,int r,int mid)
{
if (delta[now])
{
sum[now<<1]=(sum[now<<1]+delta[now]*(mid-l+1))%Mod;
sum[now<<1|1]=(sum[now<<1|1]+delta[now]*(r-mid))%Mod;
delta[now<<1]=(delta[now<<1]+delta[now])%Mod;
delta[now<<1|1]=(delta[now<<1|1]+delta[now])%Mod;
delta[now]=0;
}
}
void interval_change(int now,int l,int r,int lrange,int rrange,LL v)
{
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange)
{
sum[now]=(sum[now]+v*(r-l+1)%Mod)%Mod;
delta[now]=(delta[now]+v)%Mod;
return;
}
pushdown(now,l,r,mid);
if (lrange<=mid) interval_change(now<<1,l,mid,lrange,rrange,v);
if (mid+1<=rrange) interval_change(now<<1|1,mid+1,r,lrange,rrange,v);
update(now);
}
LL query(int now,int l,int r,int lrange,int rrange)
{
int mid=(l+r)>>1; LL ans=0;
if (lrange<=l&&r<=rrange) return sum[now];
pushdown(now,l,r,mid);
if (lrange<=mid) ans=(ans+query(now<<1,l,mid,lrange,rrange))%Mod;
if (mid+1<=rrange) ans=(ans+query(now<<1|1,mid+1,r,lrange,rrange))%Mod;
return ans;
}

LL ask(int u,int t)
{
LL ans=0;
int f1=top[u],f2=top[t];
while (f1!=f2)
{
if (h[f1]<h[f2])
{
swap(f1,f2);
swap(u,t);
}
ans=(ans+query(1,1,n,in[f1],in[u]))%Mod;
u=father[f1];
f1=top[u];
}
if (in[u]>in[t]) swap(u,t);
ans=(ans+query(1,1,n,in[u],in[t]))%Mod;
return ans;
}

int cmp(hp a,hp b)
{
return h[a.y]>h[b.y];
}

int main()
{
mi[0]=1; for (int i=1;i<=sz;++i) mi[i]=mi[i-1]*2;
scanf("%d",&n);
for (int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
addedge(x,y);
}
dfs_1(1,0,1);
dfs_2(1,0);
scanf("%d",&q);
for (int i=1;i<=q;++i)
{
scanf("%d",&opt);
if (opt==0)
{
scanf("%d%lld",&pt,&add);
interval_change(1,1,n,in[pt],out[pt],add);
}
else
{
scanf("%d",&k);
for (int i=1;i<=k;++i)
{
scanf("%d%d",&edge[i].x,&edge[i].y);
if (h[edge[i].x]>h[edge[i].y]) swap(edge[i].x,edge[i].y);
}
sort(edge+1,edge+k+1,cmp);
for (int i=1;i<k;++i)
for (int j=i+1;j<=k;++j)
{
int x1=edge[i].x,y1=edge[i].y,x2=edge[j].x,y2=edge[j].y;
if (!x1||!x2||!y1||!y2) continue;
hq r=lca(y1,y2);
if (h[x1]>h[r.fa]||h[x2]>h[r.fa]) continue;

if (h[x1]<h[x2])
{
if (r.yy==r.fa) edge[j].x=edge[j].y=0;
else edge[j].x=r.yy;
}
else
{
if (r.xx==r.fa) edge[i].x=edge[i].y=0;
else edge[i].x=r.xx;
}
}
ans=0;
for (int i=1;i<=k;++i)
if (edge[i].x&&edge[i].y)
ans=(ans+ask(edge[i].x,edge[i].y))%Mod;
printf("%lld\n",ans);
}
}
}


总结

①lca等基础算法掌握不扎实。不是不会写,而是对原理认识的不是很清楚。

②考虑问题不全面。刚开始的树枝还会加长。以后一定要想明白,判断清楚没有可能的错误再写。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: