差分与树上差分
时间:2019-09-28
本文章向大家介绍差分与树上差分,主要包括差分与树上差分使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
算法简介
差分用于解决多次区间加减法,少量单点查询的问题。
若有多次区间加减法、少量单点查询,暴力不行,线段树是通法,但不如今天要学的差分!
普通差分
普通差分就解决序列上的问题。
思想、定义与操作
1、对一个序列\(a\),设\(b[i]=a[i]-a[i-1](a[0]=0)\),则\(a[i]=\sum_{j=1}^{i} {b[j]}\);
2、对于\(a[i]到a[j]\)加上\(k\)的操作,\(b[i]+=k,b[j+1]-=k\);
3、查询时,用上面公式。
时间复杂度:修改一次是\(O(1)\),查询是\(O(N)\)。
这就是普通差分,简单得很。它一般搭配别的数据结构使用,比如树状数组什么的。
树上差分
如果是在树上对一条路径上点或边加减,怎么办?
固然可以树链剖分,但脑袋会炸,时间复杂度\(O(n\log_{2}^{2}n)\)也不利于得高分时间复杂度太高的话王逸松也没有办法啊。
所以树上差分是更好的选择。
点的差分
这是对点进行操作。
思想、定义与操作
1、\(b[i]\)表示\(i\)到根节点的路径增加的值;
2、当\(u\)到\(v\)要加\(k\)时,\(b[u]、b[v]+=k\);
3、此时\(lca(u,v)多加了一个k,其所有父节点多加了2个k\),所以\(b[lca(u,v)]-=k,b[fa[lca(u,v)]]-=k\);
4、查询时\(dfs\)一次,每个点的值为原本的值加上到根节点路径上\(b\)的和。
时间复杂度:修改一次是\(O(1)\),查询是\(O(N)\)。
代码
1、洛谷P3128 最大流
这就是模板。
#include<bits/stdc++.h>
using namespace std;
int n,a[50001],b[50001],head[50001],dep[50001],sz[50001],num;
int fa[50001],sn[50001],top[50001],id[50001],cnt,m,maxn=-2147483647;
struct E{
int next,to;
}e[100001];
inline int Read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*f;
}
void Write(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
Write(x/10);
putchar(x%10+'0');
}
void Adde(int u,int v)
{
e[++num].next=head[u];
e[num].to=v;
head[u]=num;
}
int Dfs1(int u,int f)
{
fa[u]=f;
dep[u]=dep[f]+1;
sz[u]=1;
int maxs=-1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==f)
continue;
sz[u]+=Dfs1(v,u);
if(sz[v]>maxs)
{
maxs=sz[v];
sn[u]=v;
}
}
return sz[u];
}
void Dfs2(int u,int tp)
{
id[u]=++cnt;
top[u]=tp;
if(!sn[u])
return;
Dfs2(sn[u],tp);
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(!id[v])
Dfs2(v,v);
}
}
int Lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
swap(x,y);
x=fa[top[x]];
}
if(dep[x]<dep[y])
return x;
return y;
}
/*int Queryx(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
swap(x,y);
ans+=Query(1,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y])
swap(x,y);
ans+=Query(1,id[x],id[y]);
return ans;
}
*/
void Dfs(int u,int f)
{
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==f)
continue;
Dfs(v,u);
b[u]+=b[v];
}
maxn=max(maxn,b[u]);
}
int main()
{
n=Read(),m=Read();
int u,v,lca;
for(register int i=1;i<n;++i)
{
u=Read(),v=Read();
Adde(u,v);
Adde(v,u);
}
Dfs1(1,0);
Dfs2(1,1);
for(int i=1;i<=m;++i)
{
u=Read(),v=Read();
lca=Lca(u,v);
b[u]++;
b[v]++;
b[lca]--;
b[fa[lca]]--;
}
Dfs(1,0);
Write(maxn);
return 0;
}
边的差分
不会,学了再填坑。
原文地址:https://www.cnblogs.com/SKTT1Faker/p/11560012.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法