题目链接

题目大意:给定一颗含有$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】雨天的尾巴 题解(树链剖分+树上差分)的更多相关文章

  1. BZOJ 3631 松鼠的新家 - 树链剖分 / 树上差分

    传送门 分析: 树链剖分:x->y,将x到y的路径加一,并将x端点的答案-1,最后统计答案. 树上差分:x->y,x+1,y+1,lca-1,fa[lca]-1,并将x打上标记,最后统计前 ...

  2. 【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分

    4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 703  Solved: 461[Submit][Status] ...

  3. bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】

    这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...

  4. 【CodeChef EDGEST】Edges in Spanning Trees(树链剖分+树上启发式合并)

    点此看题面 大致题意: 给你两棵\(n\)个点的树,对于第一棵树中的每条边\(e_1\),求存在多少条第二棵树中的边\(e_2\),使得第一棵树删掉\(e_1\)加上\(e_2\).第二棵树删掉\(e ...

  5. P3384 【模板】树链剖分 题解&&树链剖分详解

    题外话: 一道至今为止做题时间最长的题: begin at 8.30A.M 然后求助_yjk dalao后 最后一次搞取模: awsl. 正解开始: 题目链接. 树链剖分,指的是将一棵树通过两次遍历后 ...

  6. 洛谷P3313 [SDOI2014]旅行 题解 树链剖分+线段树动态开点

    题目链接:https://www.luogu.org/problem/P3313 这道题目就是树链剖分+线段树动态开点. 然后做这道题目之前我们先来看一道不考虑树链剖分之后完全相同的线段树动态开点的题 ...

  7. 洛谷P2486 [SDOI2011]染色 题解 树链剖分+线段树

    题目链接:https://www.luogu.org/problem/P2486 首先这是一道树链剖分+线段树的题. 线段树部分和 codedecision P1112 区间连续段 一模一样,所以我们 ...

  8. 洛谷P2146 [NOI2015]软件包管理器 题解 树链剖分+线段树

    题目链接:https://www.luogu.org/problem/P2146 本题涉及算法: 树链剖分: 线段树(区间更新及求和,涉及懒惰标记) 然后对于每次 install x ,需要将 x 到 ...

  9. 洛谷P3178 [HAOI2015]树上操作 题解 树链剖分+线段树

    题目链接:https://www.luogu.org/problem/P3178 这道题目是一道树链剖分的模板题. 但是在解决这道问题的同事刷新了我的两个认识: 第一个认识是:树链剖分不光可以处理链, ...

随机推荐

  1. python入门007

    一.深浅copy 浅拷贝:是把原列表第一层的内存地址完全拷贝一份给新列表.即只能保证对原列表中第一层地址(不可变类型)的改操作不受影响,涉及到原列表中第二层地址(可变类型)的改操作时,原列表变,新列表 ...

  2. NumPy基础知识图谱

    所有内容整理自<利用Python进行数据分析>,使用MindMaster Pro 7.3制作,emmx格式,源文件已经上传Github,需要的同学转左上角自行下载.该图谱只是NumPy的基 ...

  3. React当中的路由使用

    React 当中的路由 使用React构建的单页面应用,要想实现页面间的跳转,首先想到的就是使用路由.在React中,常用的有两个包可以实现这个需求,那就是react-router和react-rou ...

  4. 解决Linux搜狗输入法工具栏无法移动

    问题的出现 前两天一位朋友(@午后下午茶)发现一个有趣的情况: 为了复现bug,我把自己的输入法工具栏也挪到了顶栏,果然也无法挪动了 解决方法 原理不明,但解决方法很简单. 如图所示,随便找个输入框打 ...

  5. 爬虫python3:TypeError: cannot use a string pattern on a bytes-like object

    import re from common_p3 import download def crawl_sitemap(url): sitemap = download(url) links = re. ...

  6. 01 drf源码剖析之restful规范

    01 restful规范 目录 01 restful规范 1. 什么是restful规范 2.restful规范详细 1. 什么是restful规范 restful是一套规则,是程序间进行数据传输的一 ...

  7. 全栈的自我修养: 003Axios 的简单使用

    全栈的自我修养: Axios 的简单使用 You should never judge something you don't understand. 你不应该去评判你不了解的事物. 全栈的自我修养: ...

  8. 关于 iframe 的小问题若干

    我们知道,iframe在传统的MVC项目里是个很常用的东西. 但这玩意用起来有时会有点烦人. 比如说:我有个一个页面套了一个iframe,iframe里面的页面通过a标签来切换.怎么做? <li ...

  9. git常用命令操作

    git常用命令 #查看配置 git config -l #查看系统config git config --system --list #查看当前用户(global)配置 git config --gl ...

  10. 如何从Python负零基础到精通数据分析

    一.为什么学习数据分析 1.运营的尴尬:运营人需要一个硬技能每个初入行的新人都会察觉到,运营是一个似乎并没有自己的核心竞争力和安全感的工作.因为每天的工作好像都被各种琐事所围绕,而只有一个主题是永恒不 ...