【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 这道题目是一道树链剖分的模板题. 但是在解决这道问题的同事刷新了我的两个认识: 第一个认识是:树链剖分不光可以处理链, ...
随机推荐
- Howdoo中文社区AMA总结(10月21日)
10月21日Howdoo举办了中文社区的首次AMA活动,CEO -David Brierley和CMO -Jason Sibley加入到社群中与大家交流并回答社区成员的相关问题. 以下是精选的问题总结 ...
- Qt_Demo3:实现棋盘
1 简介 参考视频:https://www.bilibili.com/video/BV1XW411x7NU?p=53 说明:实现一个8*8的棋盘,点击棋盘的任意位置显示一个表情,并打印出当前的坐标( ...
- [HCTF 2018]admin
前言: 最近在BUUCTF刷题,参照师傅们wp后复现一下 0x01 拿到题目后进去如下界面 发现有登录和注册界面,相比是要登录后才能查看想要的信息. 查看页面源代码,看看有没有上面提示,界面如下 提示 ...
- DVWA学习记录 PartⅡ
Command Injection 1. 题目 Command Injection,即命令注入,是指通过提交恶意构造的参数破坏命令语句结构,从而达到执行恶意命令的目的. 2. Low a. 代码分析 ...
- 通过Windows Visual Studio远程调试WSL2中的.NET Core Linux应用程序
最近两天在Linux中调试.NET Core应用程序,同时我发现在Linux中调试.NET Core应用程序并不容易.一直习惯在Visual Studio中进行编码和调试.现在我想的是可以简单快速的测 ...
- EM算法理论与推导
EM算法(Expectation-maximization),又称最大期望算法,是一种迭代算法,用于含有隐变量的概率模型参数的极大似然估计(或极大后验概率估计) 从定义可知,该算法是用来估计参数的,这 ...
- MnasNet:经典轻量级神经网络搜索方法 | CVPR 2019
论文提出了移动端的神经网络架构搜索方法,该方法主要有两个思路,首先使用多目标优化方法将模型在实际设备上的耗时融入搜索中,然后使用分解的层次搜索空间,来让网络保持层多样性的同时,搜索空间依然很简洁,能够 ...
- Oracle DataGuard主库丢失归档日志后备库的RMAN增量恢复一例
第一部分 问题描述和环境状态确认 ----1. 问题场景 Oracle DataGuard主库丢失archivelog,如何不重建备库完成同步? 在Oracle DataGuard主从同步过程中可能 ...
- 题解 洛谷 P4143 【采集矿石】
对于一个固定的左端点,右端点向右移动时,其子串权值和不断增大,字典序降序排名不断减小,因此对于一个左端点,最多存在一个右端点使其满足条件. 所以可以枚举左端点,然后二分右端点的位置,权值和通过前缀和来 ...
- fgdsafhak