题目链接

题目大意:给定一颗含有$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. web 基础(二) HTML5

    web 基础(二) HTML5 一.HTML5 HTML5 是最新的 HTML 标准.是专门为承载丰富的 web 内容而设计的,并且无需额外插件.它拥有新的语义.图形以及多媒体元素.并提供的新元素和新 ...

  3. for of

    1. 遍历范围 for...of 循环可以使用的范围包括: 数组 Set Map 类数组对象,如 arguments 对象.DOM NodeList 对象 Generator 对象 字符串 2. 优势 ...

  4. CSS学习之选择器优先级与属性继承

    CSS学习之选择器优先级与属性继承 选择器优先级 其实选择器是具有优先级的,我们来看下面这一组案例: <!DOCTYPE html> <html lang="en" ...

  5. WPF之Converter

    1.Converter介绍 在WPF应用程序中经常遇到类似这样的问题,在定义的类中用的bool类型的值,但是界面上某个控件的显示属性是Visibility的枚举类型的,解决这个问题可以简单在定义的类中 ...

  6. 数据可视化之powerBI基础(三)编辑交互,体验更灵活的PowerBI可视化

    https://zhuanlan.zhihu.com/p/64412190 PowerBI可视化与传统图表的一大区别,就是可视化分析是动态的,通过页面上筛选.钻取.突出显示等交互功能,可以快速进行访问 ...

  7. Quartz.Net系列(十三):DateBuilder中的API详解

    1.DateOf.ToDayAt.TomorrowAt DateOf:指定年月日时分秒 public static DateTimeOffset DateOf(int hour, int minute ...

  8. Spring Boot整合swagger使用教程

    目录 Swagger的介绍 优点与缺点 添加swagger 1.添加依赖包: 2.配置Swagger: 3.测试 场景: 定义接口组 定义接口 定义接口请求参数 场景一:请求参数是实体类. 场景二:请 ...

  9. Springboot整合SpringSecurity--对静态文件进行权限管理

    文章目录 一.要求 二.依赖管理 三.配置config文件 四.扩展 一.要求 index.html 可以被所有用户访问 1.html只能被VIP1访问 2.html只能被VIP2访问 3.html只 ...

  10. STL源码剖析:算法

    启 算法,问题之解法也 算法好坏的衡量标准:时间和空间,单位是对数.一次.二次.三次等 算法中处理的数据,输入方式都是左闭又开,类型就迭代器, 如:[first, last) STL中提供了很多算法, ...