【bzoj3589】动态树 树链剖分+线段树
2017-04-06 20:00
543 查看
题目大意:
给定一棵树,支持以下两个操作:
0、子树修改
1、查询几条的链并的权值和,答案模2^31。
(这些链为某个节点到根的路径的一部分)
题目分析:(树链剖分+线段树)
首先,这道题跟动态树没什么关系。
其他的题解有用什么容斥原理,还有奇怪的动态树做的,我觉得树链剖分+线段树就足够了。
我们假设不是求这些链的并,而是求这些链的权值和。
那么只需要树链剖分之后维护线段树就可以了。
那么剩下的问题就是如何求这些链的并。
无交的链自然不用处理。
由于链的奇怪性质,有交的链必然是类似于下图的样子:
这样的链在两个最低点的lca处以上的部分相当于公共部分,所以我们完全可以转化成两个这样的链:
就是把有并的两条链拆成覆盖原来两条链的所有点的两条不想交的链。
这个我们可以枚举两条链来拆。
具体可以这样做:
求两个链底的LCA
如果这个LCA的深度低于任意一个链顶,则证明这两条链没有交。
否就把一个条链的链顶设为两个链顶中深度较浅的那个,另一条链的链顶设为LCA的下一个(详见代码)。
注意:
在枚举两条链的时候,我们相当于往原来的链的集合中新加一个,在与其他链进行合并的时候,要使当前新加进来的链能包含所有新加进来的点,所以要每次把原来的链与LCA断开。
代码如下:
附带一个小数据生成器:
给定一棵树,支持以下两个操作:
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; }
相关文章推荐
- 树链剖分 BZOJ3589 动态树
- [BZOJ3589]动态树(树链剖分)
- BZOJ 3589: 动态树 树链剖分线段树
- bzoj 1576 [Usaco2009 Jan]安全路经Travel(树链剖分,线段树)
- 【BZOJ2243】[SDOI2011]染色【树链剖分】【线段树】
- bzoj3589 动态树
- BZOJ 4034 树上操作(树链剖分 + 线段树)
- BZOJ3252 攻略 [树链剖分][不用线段树]
- 3589: 动态树|树链剖分|线段树
- bzoj2243 [SDOI2011]染色(树链剖分,线段树求颜色段数)
- BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分)(线段树单点修改)
- bzoj 2243 [SDOI2011]染色(树链剖分,线段树)
- 文章标题 BZOJ 1036 : [ZJOI2008]树的统计Count (树链剖分 + 线段树)
- 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树
- bzoj 4515: [Sdoi2016]游戏 树链剖分+线段树
- BZOJ-2243: [SDOI2011]染色 (树链剖分 入门题 线段树 区间修改查询 维护端点值)
- BZOJ 2243: [SDOI2011]染色 (树链剖分+线段树合并)
- [BZOJ3307][雨天的尾巴][树链剖分+线段树]
- bzoj3589 动态树
- [树链剖分 线段树] BZOJ 3531 [Sdoi2014]旅行