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

需要树链剖分

然后求出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. HTTP HSTS协议和 nginx

    导读 Netcraft 公司最近公布了他们检测SSL/TLS网站的研究,并指出只有仅仅5%的用户正确执行了HTTP严格传输安全HSTS.本文介绍nginx如何配置HSTS. 什么是HSTS HTTPS ...

  2. 在C/C++程序里打印调用栈信息

    我们知道,GDB的backtrace命令可以查看堆栈信息.但很多时候,GDB根本用不上.比如说,在线上环境中可能没有GDB,即使有,也不太可能让我们直接在上面调试.如果能让程序自己输出调用栈,那是最好 ...

  3. Leetcode 之Construct Binary Tree(52)

    根据先序和中序构造二叉树.根据中序和后序构造二叉树,基础题,采用递归的方式解决,两题的方法类似.需要注意的是迭代器的用法. //先序和中序 TreeNode *buildTree(vector< ...

  4. 隐藏Nginx/Apache版本号的安全性与方法

    一.先介绍nginx隐藏版本号的方法. 搭建好nginx或者apache,为了安全起见我们都会隐藏他们的版本号,这边讲的是nginx的版本号,如果你也想隐藏apache的版本号,那请点前面的链接.请看 ...

  5. HTML控件ID和NAME属性及在CS页面获得.ASPX页面中HTML控件的值

    <转载>来自网络 一.ID是在客户端脚本里用!NAME是用于获取提交表单的某表单域信息,在form里面,如果不指定Name的话,就不会发送到服务器端,所以有name属性的控件,必须指定na ...

  6. iOS 定义圆角控件

    ios7 以前,想把UILabel变为圆角的,只需要设置layer的 cornerRadius属性,ios7以后,还需要设置 masksToBounds = true. 以下是这个属性的说明 A Bo ...

  7. codeforces A. Puzzles 解题报告

    题目链接:http://codeforces.com/problemset/problem/337/A 题意:有n个学生,m块puzzles,选出n块puzzles,但是需要满足这n块puzzles里 ...

  8. LESS学习笔记1

    个人理解:less是一个可以写函数的css

  9. Ubuntu进不去,显示error:unknown filesystem (最简单解决方案总结)

    error filesysterm:文件系统错误 grub rescue:是让你拯救grub,就是你的grub坏了,引导程序坏了 要安装盘?要重装?No…… 只要几行命令就ok了 是的,这是我昨天亲自 ...

  10. 数列(codevs 1141)

    题目描述 Description 给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当k=3时,这个序列是: 1,3,4,9,10,12,13 ...