对于既要支持子树修改又要支持链查询,

需要树链剖分

然后求出DFS序,DFS的时候先DFS重儿子,

然后子树是1个区间,链是$O(\log n)$个区间

这道题对于查询若干条链的并:

由于K<=5,所以考虑容斥原理

转化为查询若干条链的交,

假设有5条链ABCDE要求交

可以先求AB的交T,再求TC的交…

考虑如何求两条树链的交:

本题中树链保证是父亲到儿子

设两条链为(a,b)(x,y),b是a的父亲,y是x的父亲

保存的交是(a',b')

c=lca(a,x)

如果c比b高或者c比y高,那么交集为空

否则a'=c

如果y在b的下面,那么b'=y,否则b'=b

每次查询$O(2^k(k\log n+\log^2n))$

常数优化:

因为对$2^{31}$取模,所以直接用int自然溢出即可,可快一倍

#include<cstdio>
#include<algorithm>
#define N 200010
#define K 17
using namespace std;
int n,i,q,x,y,k,op,ed,g[N],v[N<<1],nxt[N<<1],st[N],en[N],dfn,d[N],f[N][18],son[N],size[N],top[N],ques[6][2],ans;
inline void read(int&a){
char c;bool f=0;a=0;
while(!((((c=getchar())>='0')&&(c<='9'))||(c=='-')));
if(c!='-')a=c-'0';else f=1;
while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';
if(f)a=-a;
}
inline void addedge(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline int lca(int x,int y){
if(x==1||y==1)return 1;
if(x==y)return x;
if(d[x]<d[y])swap(x,y);
for(int i=K;~i;i--)if(d[f[x][i]]>=d[y])x=f[x][i];
if(x==y)return x;
for(int i=K;~i;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
void dfs1(int x,int pre){
size[x]=1;d[x]=d[pre]+1;
int heavy=0,sizemax=0,i;
for(f[x][0]=pre,i=1;i<=K;i++)f[x][i]=f[f[x][i-1]][i-1];
for(i=g[x];i;i=nxt[i])if(v[i]!=pre){
dfs1(v[i],x),size[x]+=size[v[i]];
if(size[v[i]]>sizemax)sizemax=size[v[i]],heavy=v[i];
}
if(heavy)son[x]=heavy;
}
void dfs2(int x,int pre,int t){
st[x]=++dfn;top[x]=t;
if(son[x])dfs2(son[x],x,t);
for(int i=g[x];i;i=nxt[i])if(v[i]!=pre&&v[i]!=son[x])dfs2(v[i],x,v[i]);
en[x]=dfn;
}
int tot,l[N<<1],r[N<<1],len[N<<1],val[N<<1],tag[N<<1];
int build(int a,int b){
int x=++tot;
len[x]=b-a+1;
if(a==b)return x;
int mid=(a+b)>>1;
l[x]=build(a,mid);r[x]=build(mid+1,b);
return x;
}
inline void add1(int x,int p){if(!x)return;val[x]+=len[x]*p;tag[x]+=p;}
inline void pb(int x){if(tag[x]!=0)add1(l[x],tag[x]),add1(r[x],tag[x]),tag[x]=0;}
inline void up(int x){val[x]=val[l[x]]+val[r[x]];}
void add(int x,int a,int b,int c,int d,int p){
if(c<=a&&b<=d){add1(x,p);return;}
int mid=(a+b)>>1;
pb(x);
if(c<=mid)add(l[x],a,mid,c,d,p);
if(d>mid)add(r[x],mid+1,b,c,d,p);
up(x);
}
int ask(int x,int a,int b,int c,int d){
if(c<=a&&b<=d)return val[x];
int mid=(a+b)>>1,t=0;
pb(x);
if(c<=mid)t+=ask(l[x],a,mid,c,d);
if(d>mid)t+=ask(r[x],mid+1,b,c,d);
up(x);
return t;
}
inline int query(int x,int y){
if(x<1)return 0;
int t=0;
while(top[x]!=top[y])t+=ask(1,1,n,st[top[x]],st[x]),x=f[top[x]][0];
return t+ask(1,1,n,st[y],st[x]);
}
inline void merge(int&a,int&b,int x,int y){
if(a==0)return;
if(a==-1){a=x,b=y;return;}
int c=lca(a,x);
if(d[c]<d[b]||d[c]<d[y]){a=b=0;return;}
a=c;
if(d[b]<d[y])b=y;
}
void dfs(int x,int a,int b,int o){
merge(a,b,ques[x][0],ques[x][1]);
int t=query(a,b);
if(!o)ans-=t;else ans+=t;
for(x++;x<=k;x++)dfs(x,a,b,o^1);
}
int main(){
read(n);
for(i=1;i<n;i++)read(x),read(y),addedge(x,y),addedge(y,x);
dfs1(1,0);dfs2(1,0,1);
build(1,n);
read(q);
while(q--){
read(op);
if(op){
read(k);
for(i=1;i<=k;i++){
read(ques[i][0]),read(ques[i][1]);
if(d[ques[i][0]]<d[ques[i][1]])swap(ques[i][0],ques[i][1]);
}
ans=0;
for(i=1;i<=k;i++)dfs(i,-1,-1,1);
if(ans<0)ans+=(~0U>>1)+1;
printf("%d\n",ans);
}else{
read(x),read(y);
add(1,1,n,st[x],en[x],y);
}
}
return 0;
}

  

BZOJ3589 : 动态树的更多相关文章

  1. bzoj3589 动态树 求链并 容斥

    bzoj3589 动态树 链接 bzoj 思路 求链并. 发现只有最多5条链子,可以容斥. 链交求法:链顶是两条链顶深度大的那个,链底是两个链底的\(lca\) 如果链底深度小于链顶,就说明两条链没有 ...

  2. [树链剖分]BZOJ3589动态树

    题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0: 这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1: 小明希望你求出几条树枝上 ...

  3. BZOJ3589 动态树(树链剖分+容斥原理)

    显然容斥后转化为求树链的交.这个题非常良心的保证了查询的路径都是到祖先的,求交就很休闲了. #include<iostream> #include<cstdio> #inclu ...

  4. bzoj千题计划214:bzoj3589: 动态树

    http://www.lydsy.com/JudgeOnline/problem.php?id=3589 树链剖分 用线段数维护扫描线的方式来写,标记只打不下传 #include<cstdio& ...

  5. BZOJ3589 动态树[树剖/暴力/容斥]

    操作0,显然直接线段树解决. 操作1,瓶颈在于重叠的链只算一次.在线段树上来看,如果一个区间被覆盖了,那么只算这个区间,子树里面也就不管了. 考虑对节点打标记来表示是否覆盖.但是,如果统一打完之后,并 ...

  6. bzoj3589 动态树 树链剖分+容斥

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=3589 题解 事件 \(0\) 不需要说,直接做就可以了. 事件 \(1\) 的话,考虑如果直接 ...

  7. BZOJ3589动态树

    **错误改了一上午. 先做熟练泼粪 k<=5,因此我们可以模拟这个过程,在线段树上把标记建出来然后pushup时候更新就好了. By:大奕哥 #include<bits/stdc++.h& ...

  8. 【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)

    3589: 动态树 Time Limit: 30 Sec  Memory Limit: 1024 MBSubmit: 405  Solved: 137[Submit][Status][Discuss] ...

  9. 【BZOJ3589】动态树 树链剖分+线段树

    Description 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0: 这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1: 小明希望你 ...

随机推荐

  1. ios流媒体

    http://my.oschina.net/CgShare/blog/302303 渐进式下载(伪流媒体) 介于下载本地播放与实时流媒体之间的一种播放形式,下载本地播放必须全部将文件下载完成后才能播放 ...

  2. django-cms 代码研究(四)CMS_TEMPLATE标签

    CMS_TEMPLATE 继承这个东东可以在实现很灵活的布局,如下:

  3. GraphicsMagick为图片添加水印

    GraphicsMagick号称图像处理领域的瑞士军刀.提供了健壮及高效的图像处理工具包和库,支持超过88种主流图片格式包括:BMP,GIF,JPEG,JPEG-2000,PNG,PDF,PNM,TI ...

  4. #!/bin/bash

    #!/bin/bash是指此脚本使用/bin/bash来解释执行. 其中,#!是一个特殊的表示符,其后,跟着解释此脚本的shell路径. bash只是shell的一种,还有很多其它shell,如:sh ...

  5. sc 与net命令的区别

    windows服务操作命令有sc和net 两个命令; sc stop serviceName  sc start serviceName net stop serviceName  net start ...

  6. 无论IT代码系统还是人生都是有惯性的

    是的,这和IT系统和代码没有什么关系:鸡汤式的文章,看烦了的就关掉吧,想看的请听我碎碎念.惯性本是物理学研究的问题,这里没有要研究物理学里的惯性.惯性无时无刻地发生在我们的日常生活中,只是你我都没有察 ...

  7. Mobile first! Wijmo 5 + Ionic Framework之:Hello World!

    本教程中,我们用Wijmo 5 和 Ionic Framework实现一个Mobile的工程:Hello World. Ionic是什么? Ionic是一个HTML5框架.免费.开源,用于帮助生成hy ...

  8. codeforces B. The Fibonacci Segment 解题报告

    题目链接:http://codeforces.com/problemset/problem/365/B 题目意思:简单来说,就是要找出最长的斐波纳契长度. 解决的方法不难,但是要注意更新左区间和右区间 ...

  9. Doodle Poll 投票文档

    使用Doodle Poll网页文件可以让大家投票看什么时间大家都合适.

  10. mysql 建表语句

    修改id为自动增长: alter table book b_ISBN int(11) auto_increment;   自动增长要加的:auto_increment 基本的sql语句: 选择:sel ...