lca总结+树上差分
lca
lca简称最近公共祖先——简介在此,不过多赘述
这里主要写的是倍增算法,oi-wiki上用的是vector,由于本人不会,只会用链表,所以这里就放链表的代码了
例题


加一个数组按倍增数组的方式存距离即可
题解——点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=1e6+10;
using namespace std;
int n,m,root,nxt[maxn<<2],to[maxn<<2],head[maxn<<2],tot,val[maxn<<2];
int cnt,dep[maxn<<2],f[maxn][20],dis[maxn][20];
void add(int x,int y,int z)
{
to[++tot]=y;
val[tot]=z;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int u,int fa,int dist)
{
dis[u][0]=dist;
f[u][0]=fa;
dep[u]=dep[fa]+1;
for(int i=1;(1<<i)<=dep[u];i++)
{
f[u][i]=f[f[u][i-1]][i-1];
dis[u][i]=dis[u][i-1]+dis[f[u][i-1]][i-1];
}
for(int i=head[u];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
dfs(y,u,val[i]);
}
}
int lca(int x,int y)
{
int res=0;
if(dep[x]>dep[y]) swap(x,y);
for(int i=17;i>=0;i--)
{
if(dep[x]+(1<<i)<=dep[y]) res+=dis[y][i],y=f[y][i];
}
if(x==y) return res;
for(int i=17;i>=0;i--)
{
if(f[y][i]!=f[x][i])
{
res+=dis[x][i];
res+=dis[y][i];
x=f[x][i];
y=f[y][i];
}
}
return dis[x][0]+dis[y][0]+res;
}
signed main()
{
scanf("%d%d",&n,&m);
char aa[2];
int x,y,z;
for(int i=1;i<=m;i++)
{
scanf("%lld%lld%lld %s ",&x,&y,&z,&aa[1]);
add(x,y,z);
add(y,x,z);
}
dfs(1,0,0);
int k;
scanf("%lld",&k);
for(int i=1;i<=k;i++)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",lca(x,y));
}
return 0;
}
树上差分
主要用途是在树上的一些统计计数操作,对树上两点路径的操作用的
主要思想
dfs深搜实现,在两端结点计数,在深搜回溯时把计数的操作从子节点传递到父节点,用下面这个图理解一下

假设我们有这样一张图,我们要在6和8两个节点间的路径加一,那我们在用树上差分时,在两端+1,会发现它们的最近公共
祖先2及以上祖先都加了2,我们的目的是让2加1,2的祖先不变,则我们需要在2处减一,2的父亲处再减一即可
题解
#include<bits/stdc++.h>
#define int long long
const int maxn=1e6+10;
using namespace std;
int n,m,root,head[maxn<<2],tot,sum;
int cnt,dep[maxn<<2],f[maxn][21],tg[maxn],a[maxn];
bool vis[maxn<<2];
struct tree{int to,val,nxt;}e[maxn<<2];
void add(int x,int y)
{
e[++tot].to=y;
e[tot].nxt=head[x];
head[x]=tot;
}
void dfs1(int u,int fa)
{
vis[u]=1;
for(int i=1;(1<<i)<=dep[u];i++)
{
f[u][i]=f[f[u][i-1]][i-1];
}
for(int i=head[u];i;i=e[i].nxt)
{
int y=e[i].to;
if(vis[y]||y==fa) continue;
f[y][0]=u;
dep[y]=dep[u]+1;
dfs1(y,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(dep[x]>=dep[y]+(1<<i)) x=f[x][i];
}
if(x==y) return x;
for(int i=20;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
void dfs(int u)
{
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt)
{
int y=e[i].to;
if(vis[y])continue;
dfs(y);
tg[u]+=tg[y];
}
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
int x,y;
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&x,&y);
add(x,y);
add(y,x);
}
dfs1(1,0);
for(int i=1;i<n;i++)
{
tg[a[i]]++;
tg[a[i+1]]++;
tg[ lca(a[i],a[i+1]) ]--;
tg[f[ lca(a[i],a[i+1]) ][0]]--;
/*
en~~,神奇的东西,相邻的加1,但从a[2]到a[3]时a[2]不用加1,这就导致除了首尾糖果都 多加了1,但到最后一个糖果时不用拿,所以只有第一个位置的糖果是对的
所以让第一个糖果数加1,最后输出时都减一就好了
*/
}
memset(vis,0,sizeof vis);
dfs(1);
tg[a[1]]++;
for(int i=1;i<=n;i++) printf("%lld\n",tg[i]-1);
return 0;
}
你已完成新手教程,下面开启困难模式
书上差分计数变形
题解——二分加树上差分
#include<bits/stdc++.h>
const int maxn=1e6+10;
using namespace std;
int n,m,root,head[maxn],tot,a[maxn],b[maxn];
int cnt,dep[maxn],f[maxn][21],dis[maxn][21],sum[maxn],l,r,ans;
bool vis[maxn];
struct tree{int to,val,nxt;}e[maxn<<2];
struct node{int a,b,anc,val;}le[maxn<<2];
void add(int x,int y,int z)
{
e[++tot].to=y;
e[tot].nxt=head[x];
e[tot].val=z;
head[x]=tot;
}
void dfs(int u,int fa)
{
vis[u]=1;
for(int i=1;(1<<i)<=dep[u];i++)
{
f[u][i]=f[f[u][i-1]][i-1];
dis[u][i]=dis[u][i-1]+dis[f[u][i-1]][i-1];
}
for(int i=head[u];i;i=e[i].nxt)
{
int y=e[i].to;
if(vis[y]||y==fa) continue;
f[y][0]=u;
b[y]=i;
dis[y][0]=e[i].val;
dep[y]=dep[u]+1;
dfs(y,u);
}
}
int lca(int x,int y)
{
if(dep[x]>dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(dep[x]+(1<<i)<=dep[y]) y=f[y][i];
}
if(x==y) return y;
for(int i=20;i>=0;i--)
{
if(f[y][i]!=f[x][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[y][0];
}
int solve(int x,int y)
{
int res=0;
if(dep[x]<dep[y])swap(x,y);
for(int i=17;i>=0;i--)
{
if(dep[x]>=dep[y]+(1<<i)) res+=dis[x][i],x=f[x][i];
}
if(x==y)return res;
for(int i=17;i>=0;i--)
{
if(f[y][i]!=f[x][i])
{
res+=dis[x][i]+dis[y][i];
x=f[x][i];
y=f[y][i];
}
}
return dis[x][0]+dis[y][0]+res;
}
void update(int now,int fa)
{
for(int i=head[now];i;i=e[i].nxt)
{
if(e[i].to!=fa)
{
update(e[i].to,now);
sum[now]+=sum[e[i].to];
}
}
}
bool check(int x)
{
int cnt=0,dec=0;
memset(sum,0,sizeof sum);
for(int i=1;i<=n;i++)
{
if(le[i].val>x)
{
cnt++;
sum[le[i].a]++;
sum[le[i].b]++;
sum[le[i].anc]-=2;
dec=max(dec,le[i].val-x);
}
}
update(1,1);
for(int i=1;i<=n;i++)
if(sum[i]==cnt&&e[b[i]].val>=dec) return 1;
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
int x,y,z;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1,0);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
le[i]={x,y,lca(x,y),solve(x,y)};
r=max(r,le[i].val);
}
r++;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))ans=r=mid;
else l=mid+1;
}
printf("%d",ans);
return 0;
}
树上差分加线段树合并
题解
#include<bits/stdc++.h>
#define lid m[id].ls
#define rid m[id].rs
const int maxn=1e5+10;
using namespace std;
int n,t,len,head[maxn],nxt[maxn<<1],to[maxn<<1],tot,cnt;
int rt[maxn],sum,s[maxn*80],ans[maxn];
struct node{int ls,rs,sum;}m[maxn*80];
int dep[maxn],f[maxn][21];
inline void add(int x,int y)
{
to[++cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;
}
inline void addm(int x,int y)
{
add(x,y);
add(y,x);
}
inline void push(int id)
{
if(!lid){m[id].sum=m[rid].sum,s[id]=s[rid];return ;}
if(!rid){m[id].sum=m[lid].sum,s[id]=s[lid];return ;}
m[id].sum=max(m[lid].sum,m[rid].sum);
s[id]=m[lid].sum>=m[rid].sum?s[lid]:s[rid];
}
inline void merge(int &a,int b,int l,int r)
{
if(!b) return;
if(!a){ a=b;return ;}
if(l==r){ m[a].sum+=m[b].sum;return ;}
int mid=(l+r)>>1;
merge(m[a].ls,m[b].ls,l,mid),merge(m[a].rs,m[b].rs,mid+1,r);
push(a);
}
inline void insert(int &id,int l,int r,int x,int y)
{
if(!id)id=++tot;
if(l==r)
{
m[id].sum+=y;
s[id]=x;
return;
}
int mid=(l+r)>>1;
if(x<=mid)insert(lid,l,mid,x,y);
else insert(rid,mid+1,r,x,y);
push(id);
}
inline void dfs(int u,int fa)
{
for(int i=1;(1<<i)<=dep[u];i++)
f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=nxt[i])
{
int y=to[i];
if(y==fa)continue;
f[y][0]=u;
dep[y]=dep[u]+1;
dfs(y,u);
}
}
inline int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;i--)
if(dep[y]+(1<<i)<=dep[x])x=f[x][i];
if(x==y)return x;
for(int i=20;i>=0;i--)
{
if(f[y][i]!=f[x][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
void as(int u,int fa)
{
for(int i=head[u];i;i=nxt[i])
{
int y=to[i];
if(y==fa)continue;
as(y,u);
merge(rt[u],rt[y],1,maxn-10);
}
ans[u]=s[rt[u]];
if(!m[rt[u]].sum) ans[u]=0;
}
int main(){
scanf("%d%d",&n,&t);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addm(x,y);
}
dfs(1,0);
for(int i=1;i<=t;i++)
{
int a,b,c,d;
scanf("%d%d%d",&a,&b,&c);
d=lca(a,b);
insert(rt[a],1,maxn,c,1),insert(rt[b],1,maxn,c,1);
insert(rt[d],1,maxn,c,-1);
insert(rt[f[d][0]],1,maxn,c,-1);
}
as(1,0);
for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);
return 0;
}
lca总结+树上差分的更多相关文章
- 洛谷P2633 Count on a tree(主席树,倍增LCA,树上差分)
洛谷题目传送门 题目大意 就是给你一棵树,每个点都有点权,每次任意询问两点间路径上点权第k小的值(强制在线). 思路分析 第k小......又是主席树了.但这次变成树了,无法直接维护前缀和. 又是树上 ...
- 【题解】POJ 3417 Network(倍增求LCA+DP+树上差分)
POJ3417:http://poj.org/problem?id=3417 思路 我们注意到由“主要边”构成一颗树 “附加边”则是非树边 把一条附加边(x,y)加入树中 会与树上x,y之间构成一个环 ...
- [luogu2680] 运输计划 (lca+二分+树上差分)
传送门 Description Input Output 一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间. Sample Input 6 3 1 2 3 1 6 4 3 1 7 4 3 ...
- [POJ3417]Network(LCA,树上差分)
Network Description Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried ...
- 洛谷P2680 运输计划 [LCA,树上差分,二分答案]
题目传送门 运输计划 Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n?1 条双向航道,每条航道建立在两个星球之间, 这 n?1 条航道连通了 L 国的所 ...
- 【BZOJ3626】LCA(树上差分,树链剖分)
题意:给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给 ...
- 【NOIP2016】天天爱跑步 题解(LCA+桶+树上差分)
题目链接 题目大意:给定一颗含有$n$个结点的树,每个结点有一个权值$w$.给定$m$条路径,如果一个点与路径的起点的距离恰好为$w$,那么$ans[i]++$.求所有结点的ans. 题目分析 暴力的 ...
- [luogu P3128][USACO15DEC]Max Flow [LCA][树上差分]
题目描述 Farmer John has installed a new system of pipes to transport milk between the stalls in his b ...
- 树上差分 (瞎bb) [树上差分][LCA]
做noip2015的运输计划写了好久好久写不出来 QwQ 于是先来瞎bb一下树上差分 混积分 树上差分有2个常用的功能: (1)记录从点i到i的父亲这条路径走过几次 (2)将每条路径(s,t ...
- [填坑]树上差分 例题:[JLOI2014]松鼠的新家(LCA)
今天算是把LCA这个坑填上了一点点,又复习(其实是预习)了一下树上差分.其实普通的差分我还是会的,树上的嘛,也是懂原理的就是没怎么打过. 我们先来把树上差分能做到的看一下: 1.找所有路径公共覆盖的边 ...
随机推荐
- Access文件清理占用内存
1.用access打开access.accdb文件 2.找到数据库工具的压缩和修复数据库,单击就行 3.数据库文件成功便成500K内存占用
- 解密数仓的SQL ON ANYWHERE技术
本文分享自华为云社区<GaussDB DWS的SQL ON ANYWHERE技术解密>,作者:tooooooooooomy. 1. 前言 适用版本:[8.1.1(及以上)] 查询分析是大数 ...
- #组合计数,卢卡斯定理#D 三元组
题目 当\(z=0\)时,\(f(x,y,z)=1\), 否则 \[f(x,y,z)=\sum_{x1=1}^x\sum_{y1=1}^y(x-x1+1)(y-y1+1)f(x1,y1,z-1) \] ...
- OpenHarmony定义组件重用样式:@Styles装饰器
如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,我们推出了可以提炼公共样式进行复用的装饰器@Styles. @St ...
- SMOKE多模式排放清单处理技术及EDGAR/MEIC清单制作与VOCs排放量核算
大气污染问题既是局部.当地的,也是区域的,甚至是全球的.本地的污染物排放除了对当地造成严重影响外,同时还会在动力输送作用下,极大地影响下风向地区的大气环境状况.数值模式模拟是分析大气污染物时空分布和成 ...
- ping 介绍
前言 因为要整理网络这一块,所以打算先把概念写下.这节介绍ping的实现原理. 正文 先看一下图: 又没有发现和我们的icmp很像?对头.在icmp中,我写道:icmp有两种报文,一种是差错报文,一种 ...
- Flutter笔记 - 事件分发
事件处理流程 Flutter 事件处理流程主要分两步,为了聚焦核心流程,我们以用户触摸事件为例来说明: 命中测试:当手指按下时,触发 PointerDownEvent 事件,按照深度优先遍历当前渲染( ...
- 第3章 python 爬虫抓包与数据解析
第 3章 Python 爬虫抓包与数据解析 3.1 抓包进阶 目前,我们已经会使用 Chrome 浏览器自带的开发者工具来抓取访问网页的数据包,但是这种抓包方法有局限性,比如只能监听一个浏览器选项卡, ...
- 合阔智云核心生产系统切换到服务网格 ASM 的落地实践
简介: 合阔智云提供了从全渠道交易管理到订单履约再到门店供应链完整的餐饮零售连锁解决方案,整个方案采取微服务设计,并深度使用了 Kubernetes 作为生产调度平台. 作者:刘如鸿 背景 合阔智 ...
- 博时基金基于 RocketMQ 的互联网开放平台 Matrix 架构实践
简介: Matrix 经过一年多的建设,目前已具备多渠道统一接入.第三方生态互联互通.基金特色交易场景化封装等功能特性.Matrix 通过建设有品质.有温度的陪伴,从技术上和体验上,让用户理解风险,理 ...