您的位置:首页 > 其它

【bzoj3589】动态树 树链剖分+线段树

2017-04-06 20:00 543 查看
题目大意:

给定一棵树,支持以下两个操作:

0、子树修改

1、查询几条的链并的权值和,答案模2^31。

(这些链为某个节点到根的路径的一部分)

题目分析:(树链剖分+线段树)

首先,这道题跟动态树没什么关系。

其他的题解有用什么容斥原理,还有奇怪的动态树做的,我觉得树链剖分+线段树就足够了。

我们假设不是求这些链的并,而是求这些链的权值和。

那么只需要树链剖分之后维护线段树就可以了。

那么剩下的问题就是如何求这些链的并。

无交的链自然不用处理。

由于链的奇怪性质,有交的链必然是类似于下图的样子:



这样的链在两个最低点的lca处以上的部分相当于公共部分,所以我们完全可以转化成两个这样的链:



就是把有并的两条链拆成覆盖原来两条链的所有点的两条不想交的链。

这个我们可以枚举两条链来拆。

具体可以这样做:

求两个链底的LCA

如果这个LCA的深度低于任意一个链顶,则证明这两条链没有交。

否就把一个条链的链顶设为两个链顶中深度较浅的那个,另一条链的链顶设为LCA的下一个(详见代码)。

注意:

在枚举两条链的时候,我们相当于往原来的链的集合中新加一个,在与其他链进行合并的时候,要使当前新加进来的链能包含所有新加进来的点,所以要每次把原来的链与LCA断开。

代码如下:

#include <cstdio>
#include <algorithm>
#include <iostream>
#define N 200001
#define ls(c) (c<<1)
#define rs(c) (c<<1|1)
using namespace std;
struct segment{
int l,r;
int sum,mark;
}seg[N<<2];
int n,m,opt,k,x,y;
int a[8],b[8];
int fir
,nes[N<<1],v[N<<1],tot=1;
int fa
,dep
,zon
,sz
,pos
,ld
,top;
void edge(int x,int y)
{
v[++tot]=y;
nes[tot]=fir[x];
fir[x]=tot;
return;
}
#define edge(x,y) edge(x,y),edge(y,x)
void dfs1(int c)
{
sz[c]=1;
dep[c]=dep[fa[c]]+1;
for(int t=fir[c];t;t=nes[t])
{
if(v[t]==fa[c]) continue;
fa[v[t]]=c;
dfs1(v[t]);
sz[c]+=sz[v[t]];
if(sz[v[t]]>sz[zon[c]]) zon[c]=v[t];
}
}
void dfs2(int c)
{
pos[c]=++top;
ld[c]=c;
if(zon[fa[c]]==c) ld[c]=ld[fa[c]];
if(zon[c]) dfs2(zon[c]);
for(int t=fir[c];t;t=nes[t])
{
if(v[t]==fa[c] || v[t]==zon[c]) continue;
dfs2(v[t]);
}
}
int lca(int x,int y)
{
while(ld[x]!=ld[y])
{
if(dep[ld[x]]<dep[ld[y]]) swap(x,y);
x=fa[ld[x]];
}
if(dep[x]<dep[y]) swap(x,y);
return y;
}
void add_mark(int c,int v)
{
seg[c].sum+=(seg[c].r-seg[c].l+1)*v;
seg[c].mark+=v;
}
void push_down(int c)
{
if(seg[c].l!=seg[c].r)
{
add_mark(ls(c),seg[c].mark);
add_mark(rs(c),seg[c].mark);
}
seg[c].mark=0;
}
void update(int c,int l,int r,int y)
{
if(l<=seg[c].l && r>=seg[c].r)
{
add_mark(c,y);
return;
}
push_down(c);
int mid=seg[c].l+seg[c].r>>1;
if(l<=mid) update(ls(c),l,r,y);
if(r>mid)  update(rs(c),l,r,y);
seg[c].sum=seg[ls(c)].sum+seg[rs(c)].sum;
}
int query(int c,int l,int r)
{
push_down(c);
if(l<=seg[c].l && r>=seg[c].r) return seg[c].sum;
int mid=seg[c].l+seg[c].r>>1;
if(r<=mid) return query(ls(c),l,r);
if(l>mid)  return query(rs(c),l,r);
return query(ls(c),l,r)+query(rs(c),l,r);
}
void build_tree(int c,int l,int r)
{
seg[c].l=l,seg[c].r=r,seg[c].sum=seg[c].mark=0;
if(l==r) return;
int mid=l+r>>1;
build_tree(ls(c),l,mid);
build_tree(rs(c),mid+1,r);
}
void query()
{
int ans=0;
for(int i=1;i<=k;i++)
{
if(dep[a[i]]>dep[b[i]]) swap(a[i],b[i]);
for(int j=1;j<i;j++)
{
if(a[j]==0 || b[j]==0) continue;
int LCA=lca(b[i],b[j]);
if(dep[LCA]<dep[a[j]] || dep[LCA]<dep[a[i]]) continue;
a[i]=dep[a[i]]<dep[a[j]]?a[i]:a[j];
if(b[j]==LCA) {a[j]=b[j]=0;continue;}
a[j]=b[j];
while(ld[a[j]]!=ld[LCA])
{
if(fa[ld[a[j]]]==LCA)
{
a[j]=ld[a[j]];
break;
}
a[j]=fa[ld[a[j]]];
}
if(ld[a[j]]==ld[LCA]) a[j]=zon[LCA];
}
}
for(int i=1;i<=k;i++)
{
if(a[i]==0 || b[i]==0) continue;
while(ld[a[i]]!=ld[b[i]])
{
ans+=query(1,pos[ld[b[i]]],pos[b[i]]);
b[i]=fa[ld[b[i]]];
}
ans+=query(1,pos[a[i]],pos[b[i]]);
}
printf("%d\n",ans&(0x7fffffff));
}
int main()
{
scanf("%d",&n);
for(int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
edge(x,y);
}
dfs1(1);dfs2(1);
build_tree(1,1,n);
scanf("%d",&m);
while(m--)
{
scanf("%d",&opt);
switch(opt)
{
case 0:
scanf("%d%d",&x,&y);
update(1,pos[x],pos[x]+sz[x]-1,y);
break;
case 1:
scanf("%d",&k);
for(int i=1;i<=k;i++)
scanf("%d%d",&a[i],&b[i]);
query();
break;
}
}
return 0;
}


附带一个小数据生成器:

#include <cstdio>
#include <ctime>
#include <algorithm>
#include <iostream>
#define N 120000
using namespace std;
int n,m;
int f
;
int fa
,dep
;
int fir
,nes[N<<1],v[N<<1],tot=1;
void edge(int x,int y)
{
v[++tot]=y;
nes[tot]=fir[x];
fir[x]=tot;
return;
}
#define edge(x,y) edge(x,y),edge(y,x)
int findie(int c)
{
if(f[c]!=c) f[c]=findie(f[c]);
return f[c];
}
void dfs(int c)
{
dep[c]=dep[fa[c]]+1;
for(int t=fir[c];t;t=nes[t])
{
if(fa[c]==v[t]) continue;
fa[v[t]]=c;
dfs(v[t]);
}
}
int main()
{
freopen("input.in","w",stdout);
srand(time(0));
n=10;
m=20;
cout<<n<<endl;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1,x,y;i<n;i++)
{
x=rand()%n+1;y=rand()%n+1;
while(findie(x)==findie(y)) x=rand()%n+1,y=rand()%n+1;
f[findie(x)]=findie(y);
edge(x,y);
cout<<x<<" "<<y<<endl;
}
dfs(1);
cout<<m<<endl;
for(int i=1,x,y,k,t;i<=m;i++)
{
int opt=rand()%2;
switch(opt)
{
case 0:
cout<<opt<<" "<<rand()%n+1<<" "<<rand()%10<<endl;
break;
case 1:
k=rand()%5+1;
cout<<opt<<" "<<k<<" ";
for(int j=1;j<=k;j++)
{
x=rand()%n+1;
t=rand()%dep[x];
y=x;
while(t--) y=fa[y];
cout<<x<<" "<<y<<" ";
}
cout<<endl;
break;
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: