操作0,显然直接线段树解决。

操作1,瓶颈在于重叠的链只算一次。在线段树上来看,如果一个区间被覆盖了,那么只算这个区间,子树里面也就不管了。

考虑对节点打标记来表示是否覆盖。但是,如果统一打完之后,并不方便计算打上标记的点的和。明确目标,现在希望能覆盖很多小区间的一个大区间被打上标记之后用他来更新答案。`````

可以对每一个点维护$acc_i$表示这个点子树内被覆盖的区间的和。那么,当有更大的区间覆盖上去的时候,直接把$acc_i$改成$sum_i$,传上去即可,同时在这个点打上已覆盖的标记。否则正常pushup。答案是$acc_{root}$

这样就保证了覆盖后答案只算一次的正确性。

还有一个问题,在操作完之后,显然应当把标记删掉。这里考虑区间覆盖性标记$tag$,当他是1的时候就是之前的完全覆盖,是-1表示没有覆盖,为了使得下一操作时清除$acc$记录,设0表示要把子树内覆盖的区间清掉。

在下放标记的时候,就可以保证左右子树以及根的$acc$都是0,能重新用。当$tag$是1的时候,可以正常下放,也可以不下放,但将0和1统一一下code更简单。复杂度$O(nklog^2n)$

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define dbg(x) cerr << #x << " = " << x <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=2e5+;
struct thxorz{int to,nxt;}G[N<<];
int hd[N],tot;
int n,q;
inline void Addedge(int x,int y){
G[++tot].to=y,G[tot].nxt=hd[x],hd[x]=tot;
G[++tot].to=x,G[tot].nxt=hd[y],hd[y]=tot;
}
#define y G[j].to
int fa[N],d[N],son[N],cnt[N],topfa[N],st[N],tim;
void dfs1(int x,int f){
fa[x]=f,d[x]=d[f]+,cnt[x]=;int tmp=-;
for(register int j=hd[x];j;j=G[j].nxt)if(y^f)dfs1(y,x),cnt[x]+=cnt[y],MAX(tmp,cnt[y])&&(son[x]=y);
}
void dfs2(int x,int topf){
topfa[x]=topf,st[x]=++tim;if(!son[x])return;dfs2(son[x],topf);
for(register int j=hd[x];j;j=G[j].nxt)if(y^fa[x]&&y^son[x])dfs2(y,y);
}
#undef y
#define lc i<<1
#define rc i<<1|1
int sumv[N<<],acc[N<<],stag[N<<],atag[N<<];
inline void Pushup(int i){sumv[i]=sumv[lc]+sumv[rc],acc[i]=acc[lc]+acc[rc];}
inline void pd_sum(int i,int L,int R){
if(stag[i]){
int mid=L+R>>;
sumv[lc]+=(mid-L+)*stag[i],sumv[rc]+=(R-mid)*stag[i],stag[lc]+=stag[i],stag[rc]+=stag[i],stag[i]=;
}
}
inline void pd_acc(int i){
if(~atag[i])
acc[lc]=atag[i]?sumv[lc]:,acc[rc]=atag[i]?sumv[rc]:,atag[rc]=atag[lc]=atag[i],atag[i]=-;
}
void Update_add(int i,int L,int R,int ql,int qr,int k){
if(ql<=L&&qr>=R){sumv[i]+=(R-L+)*k,stag[i]+=k;return;}
int mid=L+R>>;pd_sum(i,L,R);
if(ql<=mid)Update_add(lc,L,mid,ql,qr,k);
if(qr>mid)Update_add(rc,mid+,R,ql,qr,k);
Pushup(i);
}
void Update_account(int i,int L,int R,int ql,int qr){
if(ql<=L&&qr>=R){acc[i]=sumv[i],atag[i]=;return;}
int mid=L+R>>;pd_sum(i,L,R),pd_acc(i);
if(ql<=mid)Update_account(lc,L,mid,ql,qr);
if(qr>mid)Update_account(rc,mid+,R,ql,qr);
Pushup(i);
}
inline void clear_the_tree(){atag[]=,acc[]=;}
inline void Tree_query(int x,int y){
if(d[x]<d[y])x^=y^=x^=y;
while(topfa[x]^topfa[y])Update_account(,,n,st[topfa[x]],st[x]),x=fa[topfa[x]];
Update_account(,,n,st[y],st[x]);
} int main(){//freopen("test.in","r",stdin);freopen("test.ans","w",stdout);
read(n);for(register int i=,x,y;i<n;++i)read(x),read(y),Addedge(x,y);
dfs1(,),dfs2(,);
read(q);memset(atag,-,sizeof atag);
for(register int i=,opt,k,x,y;i<=q;++i){
read(opt);
if(opt){
read(k);while(k--)read(x),read(y),Tree_query(x,y);
printf("%d\n",acc[]&0x7fffffff);clear_the_tree();
}
else read(x),read(k),Update_add(,,n,st[x],st[x]+cnt[x]-,k);
}
return ;
}

还有一个点,对$2^{31}$取模用int自然溢出是因为前31位爆掉,进位到符号位,符号位会变化,但不影响前31位的mod结果。最后只要把符号位改一下即可,所以要$\text{and}2^{31}-1$。

反思:标记的表示设计想的不太好。当线段树设计有限制条件的询问时,可以维护一些附加信息,如本题的覆盖区间的和。

附:本题还有另外两种做法。

一:暴力

每次询问把跳过的每条重链全弄出来,把这些区间按dfs序从小到大排序,然后合并区间,暴力查询。复杂度$O(n(klognlog(klogn)+klog^2n))$。比较卡。

二:容斥

这个询问相当于求若干链的并,可以容斥。

$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})$

参考这篇

BZOJ3589 动态树[树剖/暴力/容斥]的更多相关文章

  1. bzoj3589 动态树 求链并 容斥

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

  2. hdu 5664 Lady CA and the graph(树的点分治+容斥)

    题意: 给你一个有n个点的树,给定根,叫你找第k大的特殊链 .特殊的链的定义:u,v之间的路径,经过题给的根节点. 题解:(来自BC官方题解) 对于求第k大的问题,我们可以通过在外层套一个二分,将其转 ...

  3. hdu 5792(树状数组,容斥) World is Exploding

    hdu 5792 要找的无非就是一个上升的仅有两个的序列和一个下降的仅有两个的序列,按照容斥的思想,肯定就是所有的上升的乘以所有的下降的,然后再减去重复的情况. 先用树状数组求出lx[i](在第 i ...

  4. Luogu4528 CTSC2008 图腾 树状数组、容斥

    传送门 设$f_i$表示$i$排列的数量,其中$x$表示不确定 那么$$ans=f_{1324}-f_{1432}-f_{1243}=(f_{1x2x}-f_{1423})-(f_{14xx}-f_{ ...

  5. hdu 5792 World is Exploding 树状数组+离散化+容斥

    World is Exploding Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Other ...

  6. Codeforces 439E Devu and Birthday Celebration 容斥

    Devu and Birthday Celebration 我们发现不合法的整除因子在 m 的因子里面, 然后枚举m的因子暴力容斥, 或者用莫比乌斯系数容斥. #include<bits/std ...

  7. BZOJ1853 Scoi2010 幸运数字 【枚举+容斥】

    BZOJ1853 Scoi2010 幸运数字 Description 在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号 ...

  8. 51nod 1486 大大走格子(容斥+dp+组合数)

    传送门 解题思路 暴力容斥复杂度太高,无法接受,考虑用\(dp\).设\(f(i)\)表示从左上角开始不经过前面的阻断点,只经过\(i\)的阻断点.那么可以考虑容斥,用经过\(i\)的总方案数减去前面 ...

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

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

随机推荐

  1. mysql innodb索引原理

    聚集索引(clustered index) innodb存储引擎表是索引组织表,表中数据按照主键顺序存放.其聚集索引就是按照每张表的主键顺序构造一颗B+树,其叶子结点中存放的就是整张表的行记录数据,这 ...

  2. 【Python】【demo实验16】【练习实例】【打印所有水仙花数】

    题目: 打印出所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数本身.例如:153是一个"水仙花数",因为153= ...

  3. Centos7 + nginx 托管 Django 项目

    使用nginx托管django服务的原理 使用uwsgi开启django服务(通过配置文件启动) 防火墙关闭uwsgi端口(uwsgi的websocket一定要使用127.0.0.1的方式配置)) 编 ...

  4. JZOJ.1150【贪心算法】IQ

    欢迎转载,请附上原链接https://www.cnblogs.com/Code-Garden/p/11276741.html(也没人会看) 一道对我来说较难的贪心题 题目描述 根据世界某权威学会的一项 ...

  5. MYSQL---触发器简单了解

    触发器 trigger 1.触发器是指事先为某张表绑定一段代码,当表中某些内容发生改变(增insert.删delete.改update)时,系统自动触发绑定的那段代码并执行.比如 一旦订单表里插入新订 ...

  6. id和class的区别

    id和class是定义css样式用到的,不同的是定义样式时的写法不一样,使用id选择样式时,定义的格式为 #main{width:20px;} ,使用class时用到的是 .main{width:20 ...

  7. 基于C#实现与JY61姿态角度传感器通信

    产品介绍:  此六轴模块采用高精度的陀螺加速度计 MPU6050,通过处理器读取 MPU6050 的测量数据 然后通过串口输出,免去了用户自己去开发 MPU6050 复杂的 IIC 协议,同时精心的 ...

  8. A Pythonic Card Deck: __len__ & __getitem__ & for 循环的嵌套

    1. 列表生成式的嵌套 for 循环: 示例如下: li1 = range(1,6) li2 = list("ABC") # list("ABC") 的结果为 ...

  9. O059、Backup Volume 操作

    参考https://www.cnblogs.com/CloudMan6/p/5662236.html   BackUp是将Volume备份到别的地方(备份设备),将来可以通过restore操作恢复. ...

  10. 用<![CDATA[]]>将xml转义为 纯文本

    被<![CDATA[]]>这个标记所包含的内容将表示为纯文本,比如<![CDATA[<]]>表示文本内容"<". 此标记用于xml文档中,我们先 ...