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

需要树链剖分

然后求出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. hiho一下 第九十六周 数论五·欧拉函数

    题目1 : 数论五·欧拉函数 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho有时候会用密码写信来互相联系,他们用了一个很大的数当做密钥.小Hi和小Ho约定 ...

  2. C#父类子类对象关系

    案例: 主要有Vehicle.cs  Airplane.cs   Car.cs  3个类. Car和Airplane都继承与Vehicle类.Vehicle中Drive为虚方法,可在子类中重写,父类引 ...

  3. 坚持不懈之linux haproxy的配置文件关键字查询手册

    1.关键词balance balance用于定义负载均衡的算法,可用于defaults.listen和backend中. balance使用方法如下: balance <algorithm> ...

  4. zookeeper 用法和日常运维

    本文以ZooKeeper3.4.3版本的官方指南为基础:http://zookeeper.apache.org/doc/r3.4.3/zookeeperAdmin.html,补充一些作者运维实践中的要 ...

  5. NOIP 2014 pj & tg

    由于我太弱,去了pj组= = ============================== T1: 傻逼暴力 T2: 傻逼暴力+判断+更新 T3: 手画一下就知道了.算出这个点在第几圈,再使劲yy下在 ...

  6. Android数据存储之SQLite的操作

    Android作为一个应用在移动设备上的操作系统,自然也就少不了数据的存储.然而SQLite作为一个轻型的关系型数据库,基于其轻量.跨平台.多语言接口及安全性等诸多因数考虑,因而Android较大的数 ...

  7. Android的Observable和iOS的NotificationCenter

    使用起来很类似,参看以下网址http://stackoverflow.com/questions/10327200/equivalent-of-ios-nsnotificationcenter-in- ...

  8. poj 2013 Symmetric Order 解题报告

    题目链接:http://poj.org/problem?id=2013 设长度非递减的字串序列为s[1]...s[n].设计递归子程序print(n),其中n为字串序号,每分析1个字串,n=n-1. ...

  9. HDU 3833 YY's new problem ()

    YY's new problem Time Limit: 12000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others ...

  10. Android中mesure过程详解

    我们在编写layout的xml文件时会碰到layout_width和layout_height两个属性,对于这两个属性我们有三种选择:赋值成具体的数值,match_parent或者wrap_conte ...