题意:给定一颗树,有 $m$ 次操作.

操作 0 :向集合 $S$ 中加入一条路径 $(p,q)$,权值为 $v$

操作 1 :给定一个点集 $T$,求 $T$ 的并集与 $S$ 中路径含交集的权和.(就是如果路径 $i$ 与 $T$ 有交集,就产生 $v_{i}$ 的贡献)

数据范围:路径长度 $\leqslant 20$,$1\leqslant n,m \leqslant 10^5$

如果路径长度为 0 (即 $S$ 中全部是点)的话我们求的就是点集 $T$ 的树链的并的权和.

这个可以用 DFS 序 + 树状数组来维护.

树上一个重要的性质就是任意两点之间如果经过 $i$ 个点的话会经过 $i-1$ 条边,边数总是点数-1.

然后下一步就特别神了:

对于新加入的一条路径:将路径上的点加上权值,边加上权值的相反数.

你发现如果一个连通块与这条路径有并集的话必经过 $i$ 个点和 $i-1$ 条边.

即边数恒等于点数 - 1,这就实现了只贡献一次的效果.

对于维护一个点到根的权和,我们采用 DFS 序 + 树状数组的方式.

#include <vector>
#include <cstdio>
#include <set>
#include <cstring>
#include <string>
#include <algorithm>
#define N 200007
#define ll long long
using namespace std;
void setIO(string s) {
string in=s+".in";
string out=s+".out";
freopen(in.c_str(),"r",stdin);
freopen(out.c_str(),"w",stdout);
}
struct BIT {
ll C[N];
int lowbit(int t) {
return t&(-t);
}
void update(int x,int v) {
while(x<N) {
C[x]+=(ll)v;
x+=lowbit(x);
}
}
ll query(int x) {
ll tmp=0;
while(x>0) {
tmp+=C[x];
x-=lowbit(x);
}
return tmp;
}
}tree;
int tot;
int n,m,L;
int edges;
int dfn;
int hd[N];
int to[N<<1];
int nex[N<<1];
int top[N];
int son[N];
int size[N];
int dep[N];
int A[N];
int fa[N];
int st[N];
int ed[N];
int Fa[N];
vector<int>G[N];
bool cmp(int a,int b) {
return st[a]<st[b];
}
void add(int u,int v) {
nex[++edges]=hd[u];
hd[u]=edges;
to[edges]=v;
}
void dfs1(int u,int ff) {
fa[u]=ff;
size[u]=1;
dep[u]=dep[ff]+1;
for(int i=hd[u];i;i=nex[i]) {
int v=to[i];
if(v==ff) {
continue;
}
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) {
son[u]=v;
}
}
}
void dfs2(int u,int tp) {
top[u]=tp;
if(son[u]) {
dfs2(son[u],tp);
}
for(int i=hd[u];i;i=nex[i]) {
if(to[i]!=fa[u]&&to[i]!=son[u]) {
dfs2(to[i],to[i]);
}
}
}
int LCA(int x,int y) {
while(top[x]!=top[y]) {
dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
}
return dep[x]<dep[y]?x:y;
}
// 到根的权和
ll Sum(int x) {
return tree.query(st[x]);
}
void build(int u) {
for(int i=hd[u];i;i=nex[i]) {
int v=to[i];
if(v==fa[u]) {
continue;
}
++tot;
Fa[tot]=u;
Fa[v]=tot;
G[u].push_back(tot);
G[tot].push_back(v);
build(v);
}
}
void dfs(int u) {
st[u]=++dfn;
for(int i=0;i<G[u].size();++i) {
int v=G[u][i];
// printf("%d %d\n",u,v);
dfs(v);
}
ed[u]=dfn;
}
int main() {
setIO("tree");
int i,j;
scanf("%d%d%d",&n,&m,&L);
for(i=1;i<n;++i) {
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(1,0);
dfs2(1,1);
tot=n;
build(1);
dfs(1);
while(m--) {
int op;
scanf("%d",&op);
if(op==0) {
int p,q,v,d=1;
scanf("%d%d%d",&p,&q,&v);
int lca=LCA(p,q);
while(p!=lca) {
tree.update(st[p],d*v);
tree.update(ed[p]+1,-d*v);
d*=-1;
p=Fa[p];
}
d=1;
while(q!=lca) {
tree.update(st[q],d*v);
tree.update(ed[q]+1,d*v);
d*=-1;
q=Fa[q];
}
tree.update(st[lca],v);
tree.update(ed[lca]+1,-v);
}
else {
int a,cnt=0;
scanf("%d",&a);
for(i=1;i<=a;++i) {
scanf("%d",&A[++cnt]);
}
scanf("%d",&a);
for(i=1;i<=a;++i) {
scanf("%d",&A[++cnt]);
}
sort(A+1,A+1+cnt,cmp);
int lca=A[1];
for(i=2;i<=cnt;++i) {
lca=LCA(lca,A[i]);
}
ll ans=0;
for(i=1;i<=cnt;++i) {
ans+=Sum(A[i])-Sum(Fa[lca]);
}
for(i=2;i<=cnt;++i) {
ans-=Sum(LCA(A[i],A[i-1]))-Sum(Fa[lca]);
}
printf("%lld\n",ans);
}
}
return 0;
}

  

模拟赛 T3 DFS序+树状数组+树链的并+点权/边权技巧的更多相关文章

  1. 洛谷P3250 [HNOI2016]网络(整体二分+树状数组+树剖)

    传送门 据说正解是树剖套堆???然而代码看着稍微有那么一点点长…… 考虑一下整体二分,设当前二分到的答案为$mid$,如果所有大于$mid$的边都经过当前点$x$,那么此时$x$的答案必定小于等于$m ...

  2. POJ 2763 (LCA +RMQ+树状数组 || 树链部分) 查询两点距离+修改边权

    题意: 知道了一颗有  n 个节点的树和树上每条边的权值,对应两种操作: 0 x        输出 当前节点到 x节点的最短距离,并移动到 x 节点位置 1 x val   把第 x 条边的权值改为 ...

  3. UOJ#291. 【ZJOI2017】树状数组 树套树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ291.html 题解 结论:这个写错的树状数组支持的是后缀加和后缀求和.这里的后缀求和在 x = 0 的时 ...

  4. 【BZOJ4785】[Zjoi2017]树状数组 树套树(二维线段树)

    [BZOJ4785][Zjoi2017]树状数组 Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一 ...

  5. [Bzoj3262]陌上花开(CDQ分治&&树状数组||树套树)

    题目链接 题目就是赤裸裸的三维偏序,所以用CDQ+树状数组可以比较轻松的解决,但是还是树套树好想QAQ CDQ+树状数组 #include<bits/stdc++.h> using nam ...

  6. BZOJ 4765 普通计算姬 dfs序+分块+树状数组+好题!!!

    真是道好题...感到灵魂的升华... 按dfs序建树状数组,拿前缀和去求解散块: 按点的标号分块,分成一个个区间,记录区间子树和 的 总和... 具体地,需要记录每个点u修改后,对每一个块i的贡献,记 ...

  7. Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)

    Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...

  8. 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树

    正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...

  9. POJ 1804 Brainman(5种解法,好题,【暴力】,【归并排序】,【线段树单点更新】,【树状数组】,【平衡树】)

    Brainman Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 10575   Accepted: 5489 Descrip ...

随机推荐

  1. (三)golang--执行流程分析

    XXX.go--go build XXX.go--XXX.exe XXX.go--go run XXX.go 两种方式的区别:(1)如果我们先编译生成了可执行文件,那么我们可以将该可执行文件拷贝到没g ...

  2. Java连载15-boolean类型&类型转换&++运算符

    一.boolean类型 1.说明: (1)在java语言中,boolean类型只有两个值:true.false,没有其他的值.在C语言中,是有0代表false和1代表true的 (2)在底层存储的时候 ...

  3. VUE引入jq bootstrap 之终极解决方案(测试)

    初入VUE遇见的一些问题,在网上找了些方法,再根据自己的实际项目解决的问题写得此文,,希望对你有所帮助. vue-cli快速构建项目以及引入boostrap.jq各种插件配置 vue-cli脚手架工具 ...

  4. centos下java环境搭建安装

    1. 购买服务器(阿里云) 2. 重置密码,重启服务器 3. 创建账号work groupadd work #创建组 mkdir /data # 创建数据文件夹 useradd -d /data/wo ...

  5. c++小学期大作业攻略(四)任务系统+站内信

    虽然比最早的预定晚了整整一个星期但这核心功能最后一篇终于还是来了. 如果你已经经历了用户系统的洗礼,相信代码实现应该已经没有太大的难度,所以我们重点关注一下设计好的流程. 一.任务系统 首先是新建任务 ...

  6. Spark Core知识点复习-1

    Day1111 Spark任务调度 Spark几个重要组件 Spark Core RDD的概念和特性 生成RDD的两种类型 RDD算子的两种类型 算子练习 分区 RDD的依赖关系 DAG:有向无环图 ...

  7. SQL 自动生成序号

    查询出来的数据的编号,不是按照从1开始的有序进行的,界面上显示想显示有序排序 select RANK() OVER(ORDER BY id ) as ID ,* from reconcilet_det ...

  8. loadrunner:Action.c(4): Error -27796: Failed to connect to server "10.8.251.101:10086": [10060] Connection timed out

    Action.c(4): Error -27796: Failed to connect to server "10.8.251.101:10086": [10060] Conne ...

  9. P2704 [NOI2001]炮兵阵地 (状压DP)

    题目: P2704 [NOI2001]炮兵阵地 解析: 和互不侵犯一样 就是多了一格 用\(f[i][j][k]\)表示第i行,上一行状态为\(j\),上上行状态为\(k\)的最多的可以放的炮兵 发现 ...

  10. Linux 常用命令 , 其他名 , 文件管理

    Linux 常用命令 , 其他名 , 文件管理 一丶Linux常用的指令 1. bsystemctl stop firewalld #关闭防火墙 2. iptables -F #清空防火墙规则 3. ...