Portal --> bzoj3589

Description

  给你一棵\(n\)个节点的树,总共有\(q\)次操作,每次操作是以下两种中的一种:

操作\((0,x,delta)\):给以\(x\)为根的子树中每个节点的点权\(+delta\)

操作\((1,k,(x_1,y_1),(x_2,y_2)...(x_k,y_k))\):求\(k\)条链的并的权值之和,一个节点集合的权值之和定义为该集合中所有节点的点权之和

  点权初始为\(0\),链保证是某个节点到根的路径上的某一段

​  数据范围:\(1<=n,q<=2*10^5,1<=k<=5\)

Solution

  这题的话。。虽然说网上好像有根本不需要用到容斥和链的特性直接一个线段树就可以搞定的做法。。但是我不会qwq所以还是容斥吧qwq

​  因为这题中的链有十分优秀的性质,都是到根路径上的某一段,所以我们考虑维护每一个节点到根的路径上的点权和,这样一旦求得并问题就很好解决了

​  子树加操作的话直接用树剖+线段树或者树状数组来搞就好了

​  线段树直接搞就不讲了,如果用树状数组的话就是开两个支持区间修改单点查询的树状数组

​  我们考虑对于一次修改操作\((x,delta)\),\(y\)是\(x\)子树中的一个节点,考虑这次修改对\(y\)这个位置的值的影响,应该是加上\((dep[y]-(dep[x]-1))*delta\)

​  那么我们可以考虑前面的\(dep[y]*delta\)和\(-(dep[x]-1)*delta\)分开维护

​  我们在一个BIT(记为BIT1)中的\(x\)子树范围内的每个节点\(+(dep[x]-1)*delta\),然后在另一个BIT(记为BIT2)中的\(x\)子树范围内每个节点\(+delta\)

​  最后查询就直接BIT2::query(x)*dep[x]-BIT1::query(x),就能够得到\(x\)到根路径上所有节点的权值和了(当然你也可以第二个BIT修改的时候是\(+dep[x]*delta\),查询的时候就不用在外面乘\(dep[x]\),一样的)

​​  

​  接下来就是求并了

  

​  首先注意到每次询问的这个链的数量十分少,并且因为这个链必定是某个节点到根的路径上的一段,所以求两条这样的链的交集其实是很容易的(求个lca然后判断一下什么的就好了)

​  但是求并是一个。。很困难的过程。。

  所以我们考虑用容斥将求并转化为求交,具体一点的话就是集合容斥的这条式子:

\[|\bigcup\limits_{i=1}^{n}A_i|=\sum\limits_{k=1}^{n}(-1)^{k-1}\sum\limits_{1<=j_1<j_2<...<j_k<=n}|A_{j_1}\cap A_{j_2}\cap ...\cap A_{j_k}|
\]

  只不过我们这里用来容斥的东西不是集合的大小而是集合中元素的和:

\[sum(\bigcup\limits_{i=1}^{n}A_i)=\sum\limits_{k=1}^{n}(-1)^{k-1}\sum\limits_{1<=j_1<j_2<...<j_k<=n}sum(A_{j_1}\cap A_{j_2}\cap ...\cap A_{j_k})
\]

  然后链的数量又特别小所以直接\(2^{num}\)枚举一下就好了(其中\(num\)表示的是链的数量)

  (这里提醒一下自己。。枚举的时候可以用状压的方式来操作,这样比dfs不知道高到哪里去了qwq)

  最后还有一点就是,对\(2^{31}\)取模可以int自然溢出最后再和maxlongint取个与就好了

  

​  代码大概长这个样子

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=2*(1e5)+10,TOP=20;
struct xxx{
int y,nxt;
}a[N*2];
struct L{
int x,y;
L(){}
L(int _x,int _y){x=_x; y=_y;}
}rec[6],tmp;
int h[N],lis[N*2],st[N],dep[N],pre[N];
int mn[N*2][TOP+1];
int dfn[N],dfned[N];
int n,m,tot,dfn_t,dfn_t1;
namespace BIT{/*{{{*/
int c[N*2],rt[2];
int n;
void init(int _n){n=_n;rt[0]=0; rt[1]=n;}
void _add(int St,int x,int delta){
for (;x<=n;x+=x&-x) c[St+x]+=delta;
}
void add(int which,int x,int delta){_add(rt[which],dfn[x],delta);_add(rt[which],dfned[x]+1,-delta);}
int _query(int St,int x){
int ret=0;
for (;x;x-=x&-x) ret+=c[St+x];
return ret;
}
int query(int x){
if (!x) return 0;
return _query(rt[1],dfn[x])*dep[x]-_query(rt[0],dfn[x]);
}
}/*}}}*/
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void dfs(int fa,int x,int d){
int u;
st[x]=++dfn_t; lis[dfn_t]=x; dep[x]=d; pre[x]=fa;
dfn[x]=++dfn_t1;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
dfs(x,u,d+1);
lis[++dfn_t]=x;
}
dfned[x]=dfn_t1;
}
void prework(){
BIT::init(n);
lis[0]=dfn_t;
for (int i=1;i<=lis[0];++i) mn[i][0]=lis[i];
for (int j=1;j<=TOP;++j)
for (int i=lis[0]-(1<<j)+1;i>=1;--i)
if (dep[mn[i][j-1]]<dep[mn[i+(1<<j-1)][j-1]])
mn[i][j]=mn[i][j-1];
else
mn[i][j]=mn[i+(1<<j-1)][j-1];
}
int get_lca(int x,int y){
x=st[x]; y=st[y];
if (x>y) swap(x,y);
int len=y-x+1,lg=(int)(log(1.0*len)/log(2.0));
if (dep[mn[x][lg]]<dep[mn[y-(1<<lg)+1][lg]]) return mn[x][lg];
else return mn[y-(1<<lg)+1][lg];
}
void merge(L &x,L y){
if (x.x==0&&x.y==0) return;
int lca;
if (dep[x.x]<dep[y.x]){
lca=get_lca(x.y,y.x);
if (lca!=y.x){x=L(0,0); return;}
lca=get_lca(x.y,y.y);
x=L(y.x,lca);
}
else{
lca=get_lca(y.y,x.x);
if (lca!=x.x){x=L(0,0); return;}
lca=get_lca(y.y,x.y);
x=L(x.x,lca);
}
}
int get_val(L x){
return BIT::query(x.y)-BIT::query(pre[x.x]);
}
void solve(int num){
int all=1<<num,mark,ans=0;
for (int i=1;i<all;++i){
mark=-1;
tmp=L(-1,-1);
for (int j=1;j<=num;++j)
if (i>>(j-1)&1){
mark*=-1;
if (tmp.x==-1&&tmp.y==-1)
tmp.x=rec[j].x,tmp.y=rec[j].y;
else
merge(tmp,rec[j]);
}
ans+=mark*get_val(tmp);
}
printf("%d\n",ans&2147483647);
}
void debug(){
for (int i=1;i<=n;++i) printf("%d ",BIT::query(i));
printf("\n");
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,delta,num,op;
scanf("%d",&n);
memset(h,-1,sizeof(h));
tot=0;
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfn_t=0; dfn_t1=0;
dfs(0,1,1);
prework();
scanf("%d",&m);
for (int i=1;i<=m;++i){
scanf("%d",&op);
if (op==0){
scanf("%d%d",&x,&delta);
BIT::add(0,x,(dep[x]-1)*delta);
BIT::add(1,x,delta);
}
else{
scanf("%d",&num);
for (int j=1;j<=num;++j){
scanf("%d%d",&rec[j].x,&rec[j].y);
if (dep[rec[j].x]>dep[rec[j].y]) swap(rec[j].x,rec[j].y);
}
solve(num);
}
//debug();
}
}

【bzoj3589】动态树的更多相关文章

  1. bzoj3589 动态树 求链并 容斥

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

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

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

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

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

  4. BZOJ3589 : 动态树

    对于既要支持子树修改又要支持链查询, 需要树链剖分 然后求出DFS序,DFS的时候先DFS重儿子, 然后子树是1个区间,链是$O(\log n)$个区间 这道题对于查询若干条链的并: 由于K<= ...

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

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

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

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

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

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

  8. BZOJ3589动态树

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

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

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

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

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

随机推荐

  1. TensorFlow深度学习实战---图像识别与卷积神经网络

    全连接层网络结构:神经网络每两层之间的所有结点都是有边相连的. 卷积神经网络:1.输入层 2.卷积层:将神经网络中的每一个小块进行更加深入地分析从而得到抽象程度更高的特征. 3 池化层:可以认为将一张 ...

  2. lxd&openstack-lxd源码剖析

    lxd:https://linuxcontainers.org/lxd/,目标是融入到openstack体系被管理,像虚拟机一样被管理使用.从如下图可知,并非走的是libvirt-lxc路线,而是no ...

  3. JAVA学习笔记--数组初始化

    JAVA中,数组只是相同类型的.用一个标识符名称封装到一起的一个对象序列或基本类型数据序列.数组通过方括号下标操作符[]来定义和使用,要定义一个数组只需在类型名后面加上一个方括号即可,如: int[] ...

  4. 梯度消失&&梯度爆炸

    转载自: https://blog.csdn.net/qq_25737169/article/details/78847691 前言 本文主要深入介绍深度学习中的梯度消失和梯度爆炸的问题以及解决方案. ...

  5. Python坑系列:可变对象与不可变对象

    在之前的文章 http://www.cnblogs.com/bitpeng/p/4748148.html 中,大家看到了ret.append(path) 和ret.append(path[:])的巨大 ...

  6. Chrome 鲜为人知的秘籍(内部协议)&&Chrome功能指令大全

    楼主以 Chrome 版本 39.0.2171.95 m 为例,耗费2小时的记录: chrome://accessibility 用于查看浏览器当前访问的标签,打开全局访问模式可以查看:各个标签页面的 ...

  7. 常用的不易记忆的css自定义代码

    在制作页面时,经常会遇到需要自定义一些标签的默认行为(如:input的占位符等),但这些默认的设置的css一般比较难记住,所以有必要自己做一下记录.下面是我经常用到的一些重设默认行为的css. 1.占 ...

  8. curl和file_get_contents 区别以及各自的优劣

    PHP中fopen,file_get_contents,curl函数的区别: 1.fopen /file_get_contents 每次请求都会重新做DNS查询,并不对 DNS信息进行缓存.但是CUR ...

  9. EXT4.1表单提交(非AJAX)

    Ext.require([ 'Ext.form.*', 'Ext.data.*' ]); Ext.onReady(function(){ Ext.apply(Ext.form.VTypes, { re ...

  10. 软工alpha阶段个人总结

    一.个人总结 类别 具体技能和面试问题 现在的回答(大三下) 语言 最拿手的语言之一,代码量是多少? java,代码量在一万行左右 语言 最拿手的语言之二,代码量是多少? C语言,代码量在五千行左右 ...