【BZOJ3307】雨天的尾巴 题解(树链剖分+树上差分)
题目大意:给定一颗含有$n$个结点的树,每次选择两个结点$x$和$y$,对从$x$到$y$的路径上发放一带$z$类型的物品。问完成所有操作后每个结点发放最多的时哪种物品。
普通的树链剖分貌似也可以做这道题,可以记录一个$c$数组用来记录结点中每种物品的个数,然后暴力乱搞。空间可能会炸。
这时候我们需要一种新算法:树上差分。
关于树上差分,有需要的同学可以去看大佬的博客,我这里说一下思想。
对于序列的差分,我们都知道,假设让序列中$i-j$的数都加上$z$,那么直接让差分数组$b[i]+=z$,$b[j]-=z$即可。放到一颗树中(其实这里变成了子树和),让路径上$s-t$的结点都加上$k$,我们只需在$s,t$加上$k$,在$lca(s,t),fa[lca(s,t)]$减去$k$即可。实际上树上差分的实现形式多种多样,我们可以根据题目需要来作改动。
对于此题,我们不妨用$\log n$的时间把路径$x,y$拆分成$[a_1,b_1],[a_2,b_2],\cdots ,[a_k,b_k]$这样多个区间的形式,每次在$a_i$处加上$z$,在$b_i+1$处减去$z$。写起来是这样的:
inline void chai(int x,int y,int c)
{
while(top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
Add(dfn[top[x]],c);Add(dfn[x]+,-c);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
Add(dfn[x],c);Add(dfn[y]+,-c);
}
个数可以用线段树维护。其实正解应该是动态开点+线段树合并之类的高级算法,但是我不会QAQ。所以只写了较为普通的线段树维护。时间复杂度$n\log n$。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=;
int n,m;
int size[maxn],son[maxn],dep[maxn],fa[maxn];
int dfn[maxn],cnt,w[maxn],top[maxn],ans[maxn];//w[dfn[x]]=x; ans[w[i]]=sum;
int head[maxn*],jishu;
int Head[maxn*],Jishu;
struct node
{
int next,to,val;
}edge[maxn*],Edge[maxn*];//Edge chai
struct tre
{
int maxx,pos;
}tree[maxn*];
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)){if (ch=='-') f=-;ch=getchar();}
while(isdigit(ch)){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void add(int from,int to)
{
edge[++jishu].next=head[from];
edge[jishu].to=to;
head[from]=jishu;
}
inline void Add(int from,int val)
{
Edge[++Jishu].next=Head[from];
Edge[Jishu].val=val;
Head[from]=Jishu;
}
inline void dfs_son(int now,int f,int deep)
{
dep[now]=deep;
fa[now]=f;
size[now]=;
int maxson=-;
for (int i=head[now];i;i=edge[i].next)
{
int to=edge[i].to;
if (to==f) continue;
dfs_son(to,now,deep+);
size[now]+=size[to];
if (maxson<size[to]) maxson=size[to],son[now]=to;
}
}
inline void dfs(int now,int tp)
{
dfn[now]=++cnt;
top[now]=tp;
w[cnt]=now;
if (son[now]) dfs(son[now],tp);
for (int i=head[now];i;i=edge[i].next)
{
int to=edge[i].to;
if (dfn[to]) continue;
dfs(to,to);
}
}
inline void pushup(int index)
{
if (tree[index*].maxx>=tree[index*+].maxx) tree[index].maxx=tree[index*].maxx,tree[index].pos=tree[index*].pos;
else tree[index].maxx=tree[index*+].maxx,tree[index].pos=tree[index*+].pos;
}
inline void build(int index,int l,int r)
{
if (l==r){tree[index].maxx=;tree[index].pos=l;return;}
int mid=(l+r)>>;
build(index*,l,mid);
build(index*+,mid+,r);
pushup(index);
}
inline void update(int index,int l,int r,int pos,int k)
{
if (l==r){tree[index].maxx+=k;return;}
int mid=(l+r)>>;
if (pos<=mid) update(index*,l,mid,pos,k);
if (pos>mid) update(index*+,mid+,r,pos,k);
pushup(index);
}
inline void chai(int x,int y,int c)
{
while(top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
Add(dfn[top[x]],c);Add(dfn[x]+,-c);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
Add(dfn[x],c);Add(dfn[y]+,-c);
}
signed main()
{
n=read(),m=read();
for (int i=;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
dfs_son(,,);
dfs(,);
for (int i=;i<=m;i++)
{
int x=read(),y=read(),z=read();
chai(x,y,z);
}
build(,,);
//for (int i=1;i<=n;i++) cout<<w[i]<<endl;
for (int i=;i<=n;i++)
{
for (int j=Head[i];j;j=Edge[j].next)
{
if (Edge[j].val>) update(,,,Edge[j].val,);
else update(,,,-Edge[j].val,-);
}
ans[w[i]]=tree[].maxx?tree[].pos:;
}
//cout<<Jishu;
for (int i=;i<=n;i++) printf("%lld\n",ans[i]);
return ;
}
【BZOJ3307】雨天的尾巴 题解(树链剖分+树上差分)的更多相关文章
- BZOJ 3631 松鼠的新家 - 树链剖分 / 树上差分
传送门 分析: 树链剖分:x->y,将x到y的路径加一,并将x端点的答案-1,最后统计答案. 树上差分:x->y,x+1,y+1,lca-1,fa[lca]-1,并将x打上标记,最后统计前 ...
- 【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分
4326: NOIP2015 运输计划 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 703 Solved: 461[Submit][Status] ...
- bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】
这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...
- 【CodeChef EDGEST】Edges in Spanning Trees(树链剖分+树上启发式合并)
点此看题面 大致题意: 给你两棵\(n\)个点的树,对于第一棵树中的每条边\(e_1\),求存在多少条第二棵树中的边\(e_2\),使得第一棵树删掉\(e_1\)加上\(e_2\).第二棵树删掉\(e ...
- P3384 【模板】树链剖分 题解&&树链剖分详解
题外话: 一道至今为止做题时间最长的题: begin at 8.30A.M 然后求助_yjk dalao后 最后一次搞取模: awsl. 正解开始: 题目链接. 树链剖分,指的是将一棵树通过两次遍历后 ...
- 洛谷P3313 [SDOI2014]旅行 题解 树链剖分+线段树动态开点
题目链接:https://www.luogu.org/problem/P3313 这道题目就是树链剖分+线段树动态开点. 然后做这道题目之前我们先来看一道不考虑树链剖分之后完全相同的线段树动态开点的题 ...
- 洛谷P2486 [SDOI2011]染色 题解 树链剖分+线段树
题目链接:https://www.luogu.org/problem/P2486 首先这是一道树链剖分+线段树的题. 线段树部分和 codedecision P1112 区间连续段 一模一样,所以我们 ...
- 洛谷P2146 [NOI2015]软件包管理器 题解 树链剖分+线段树
题目链接:https://www.luogu.org/problem/P2146 本题涉及算法: 树链剖分: 线段树(区间更新及求和,涉及懒惰标记) 然后对于每次 install x ,需要将 x 到 ...
- 洛谷P3178 [HAOI2015]树上操作 题解 树链剖分+线段树
题目链接:https://www.luogu.org/problem/P3178 这道题目是一道树链剖分的模板题. 但是在解决这道问题的同事刷新了我的两个认识: 第一个认识是:树链剖分不光可以处理链, ...
随机推荐
- day52 html进阶
目录 一.分组与嵌套 二.伪类选择器 三.伪元素选择器 四.选择器优先级 五.css属性相关 1 字体属性 2 文字属性 3 背景图片 4 边框 5 display属性 6 盒子模型 7 浮动 一.分 ...
- 论TEMP临时变量与VAR静态变量区别
TEMP临时变量:顾名思义,这种变量类型是临时的,没有固定的存放数据的内存空间.每次扫描结束后则清零,在下个扫描周期开始时,这个变量的值都是不确定的,一般为0.使用临时变量需要遵循一个原则:先赋值再使 ...
- java 面向对象(三十九):反射(三)了解ClassLoader
1.类的加载过程----了解 2.类的加载器的作用 3.类的加载器的分类 4.Java类编译.运行的执行的流程 5.使用Classloader加载src目录下的配置文件 @Test public vo ...
- redis(十一):Redis 列表(List) (python)
# -*- coding: utf-8 -*- import redis r =redis.Redis(host="123.156.74.190",port=6379,passwo ...
- Python网络编程04 /recv工作原理、展示收发问题、粘包现象
Python网络编程04 /recv工作原理.展示收发问题.粘包现象 目录 Python网络编程04 /recv工作原理.展示收发问题.粘包现象 1. recv工作原理 2. 展示收发问题示例 发多次 ...
- 设计模式:fly weight模式
目的:通过共享实例的方式来避免重复的对象被new出来,节约系统资源 别名:享元模式 例子: class Char //共享的类,轻量级 { char c; public: Char(char c) { ...
- Shell基本语法---while语句
while语句 格式 while [ 条件判断式 ] do 执行动作 done 例子 i= while [ $i -gt ] do echo $i i=$((i - )) done
- 想进大厂?字节跳动等独角兽公司都在招募Python工程师!(Python就是第一语言)
在本文章中,作者通过自身经历,力求客观的谈谈个人选择学习Python的动机,以及独角兽公司对Python工程师的要求及薪资. 从目前各种迹象(企业招聘,语言排名等)看来Python相对Java应该是暂 ...
- 关于ajaxSubmit传递参数 后台接收为"参数,参数”的问题
问题: 用户名密码往后台提交的时候,发现接收到的参数变成了下图 解决办法: 去掉ajaxSubmit的data属性 如下图 解释:因为ajaxSubmit在封装的时候会自动的从被form包裹的表单控件 ...
- Zabbix-server自动发现,批量添加主机,并链接模板
zabbix可以手动添加agent客户端,当主机数量比较多时,这时手工重复工作会大大增加.zabbix的自动发现功能可以帮我们解决这个问题. 准备条件: 1. 被监控主机都装上zabbix-agent ...