序列/树上差分小结 By cellur925
首先我们需要注意一下的是,差分比较适用于修改比较多而查询比较少的情况。
一、序列上差分
借教室 这是一道二分答案,在check函数中用到差分技巧的一道题,譬如说我们要把一个序列中[l,r]区间都加上一个权值,我们可以把在 l 处加上这个值,在r+1处减去这个值,再对记录权值的数组求前缀和,那么我们就可以得到这个真正的权值数组。
题解 在链接里,代码就不放了=w=。
二、树上差分
树上差分可以分为在点权上的情况和 在边权上的情况。
1: 点权 :
比如在树上把 从u到v的路径的某个权值都加上一个数,设这个权值数组val,那么我们需要在val[u],val[v]上加上这个值,并在val[lca(u,v)]上减去这个值,并在val[f[lca(u,v][0]]上减去这个值。
理论如斯。
这个算法就经常用于统计树上路径某点/某边的经过次数。
例题1 [USACO15DEC]最大流Max Flow(不是网络流题目啦==)
给出若干路径,求被经过此处最多的点的经过次数。
Sol:树上倍增求LCA+树上差分记录+最后O(n)扫一遍更新。
总复杂度O(nlogn预处理+klogn求LCA+n更新)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 500900 using namespace std; int n,k,t,ans,tot;
int head[maxn],d[maxn],val[maxn],f[maxn][];
struct node{
int to,next;
}edge[maxn*]; void add(int x,int y)
{
edge[++tot].next=head[x];
head[x]=tot;
edge[tot].to=y;
} void init_LCA()
{
queue<int>q;
q.push();
d[]=;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(d[v]) continue;
d[v]=d[u]+;
f[v][]=u;
for(int j=;j<=t;j++)
f[v][j]=f[f[v][j-]][j-];
q.push(v);
}
}
} int LCA(int x,int y)
{
if(d[x]>d[y]) swap(x,y);
for(int i=t;i>=;i--)
if(d[f[y][i]]>=d[x]) y=f[y][i];
if(x==y) return x;
for(int i=t;i>=;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][];
} void review(int u,int fa)
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa) continue;
review(v,u);
val[u]+=val[v];
}
ans=max(ans,val[u]);
} int main()
{
scanf("%d%d",&n,&k);
t=log2(n)+;
for(int i=;i<=n-;i++)
{
int x=,y=;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
init_LCA();
while(k--)
{
int x=,y=;
scanf("%d%d",&x,&y);
int fa=LCA(x,y);
// printf("%d\n",fa);
val[x]++;val[y]++;
val[fa]--;val[f[fa][]]--;
}
review(,);
printf("%d",ans);
return ;
}
例题2 [JLOI2014]松鼠的新家
也是求点经过次数的=w=,稍有变动,需要在最后从第2个到达点到最后一个到达点的权值都减。(题意使然)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 300090 using namespace std; int n,tot,t;
int seq[maxn],head[maxn],val[maxn],d[maxn],f[maxn][];
struct node{
int to,next;
}edge[maxn*]; void add(int x,int y)
{
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
} void LCA_prework()
{
queue<int>q;
q.push(),d[]=;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(d[v]) continue;
d[v]=d[u]+;
f[v][]=u;
for(int j=;j<=t;j++)
f[v][j]=f[f[v][j-]][j-];
q.push(v);
}
}
} int LCA(int x,int y)
{
if(d[x]>d[y]) swap(x,y);
for(int i=t;i>=;i--)
if(d[f[y][i]]>=d[x]) y=f[y][i];
if(x==y) return x;
for(int i=t;i>=;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][];
} void review(int u,int fa)
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa) continue;
review(v,u);
val[u]+=val[v];
}
} int main()
{
scanf("%d",&n);
t=log2(n)+;
for(int i=;i<=n;i++) scanf("%d",&seq[i]);
for(int i=;i<=n-;i++)
{
int x=,y=;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
LCA_prework();
for(int i=;i<=n-;i++)
{
int fa=LCA(seq[i],seq[i+]);
val[seq[i]]++;val[seq[i+]]++;
val[fa]--;val[f[fa][]]--;
}
review(,);
for(int i=;i<=n;i++) val[seq[i]]--;
for(int i=;i<=n;i++) printf("%d\n",val[i]);
return ;
}
(话说省选出板子题真的好么==)
2: 边权:
给你一棵树,有n次修改操作,每次把u..v的路径权值加x,最后问从x..y的路径权值和。
例如有一次操作是把红点(u)到绿点(v)之间的路径全部加x。那么我就标记dlt[u]+=x,dlt[v]+=x。然后我们要在lca(u,v)处标记dlt[lca(u,v)]-=2x。这样就使得加x的效果只局限在u..v,不会向lca(u,v)的爸爸蔓延。
上面的话引用自@Sagittariusdalao。
例题:NOIp2015运输计划
val[x]就是x与它的父节点之间的“树边”被覆盖的次数
链接中有详细的题解==
小结:树上差分和树上倍增还是比较实用的,也比较灵活,需要加强举一反三能力==
序列/树上差分小结 By cellur925的更多相关文章
- poj3417 闇の連鎖 【树上差分】By cellur925
闇の連鎖(yam.pas/c/cpp)题目描述传说中的暗之连锁被人们称为 Dark.Dark 是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它.经过研究,你发现 Dark 呈现无向图的结构,图中有 ...
- [GXOI/GZOI2019]旧词(树上差分+树剖)
前置芝士:[LNOI2014]LCA 要是这题放HNOI就好了 原题:\(\sum_{l≤i≤r}dep[LCA(i,z)]\) 这题:\(\sum_{i≤r}dep[LCA(i,z)]^k\) 对于 ...
- BZOJ2588 主席树 + 树上差分
https://www.lydsy.com/JudgeOnline/problem.php?id=2588 题意:强制在线的询问树链权值第K小(无修) 这种类似于第K小的题,一般容易想到主席树,但是树 ...
- bzoj4326 树链剖分 + 线段树 // 二分 lca + 树上差分
https://www.lydsy.com/JudgeOnline/problem.php?id=4326 题意:N个点的树上给M条树链,问去掉一条边的权值之后所有树链长度和的最大值最小是多少. 首先 ...
- 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)
线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...
- BZOJ3881 Coci2015Divljak(AC自动机+树上差分+树状数组)
建出AC自动机及其fail树,每次给新加入的串在AC自动机上经过的点染色,问题即转化为子树颜色数.显然可以用dfs序转成序列问题树状数组套权值线段树解决,显然过不掉.事实上直接树上差分,按dfs序排序 ...
- [JLOI2014]松鼠的新家 树上差分
差分 一开始竟然想分情况讨论来差分,然后发现各自情况要分析, 就是为了解决中间节点重复计算的问题, 结果 最后一想,中间重复计算了一次,那我最后减掉不就好了么,,, 那这就是一道差分裸题了(这是唯一不 ...
- 差分数组 and 树上差分
差分数组 定义 百度百科中的差分定义 //其实这完全和要讲的没关系 qwq 进去看了之后是不是觉得看不懂? 那我简单概括一下qwq 差分数组de定义:记录当前位置的数与上一位置的数的差值. 栗子 容易 ...
- Codeforces E. Alyona and a tree(二分树上差分)
题目描述: Alyona and a tree time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
随机推荐
- Java中常见的注解
Java中常见的注解 1.JDK自带的注解@Override @Deprecated @Suppvisewarnings 常见第三方注解 Spring:@Autowired @Service ...
- MySQL中给自定义的字段查询结果添加排名的方法
我正在用 MySQL 客户端的时候,突然想到如果可以给查询结果添加排名该多好啊,然后就找到了一个简单的解决办法. 下面是一个示例表的数据: 然后我们要根据 Roll_No 字段进行排序并给出排名,我 ...
- Ehcache CacheManager
CacheManager是Ehcache框架的核心类和入口,它负责管理一个或多个Cache对象.要使用Ehcache框架,必须要先创建 CacheManager 对象.现在我们学习下,如何创建 Cac ...
- Javascript正则中的exec和match
分几种情况说明 1.假设re中不是全局的也就是不带g var str = "cat3 hat4"; var re = /\w+\d/; var ex = re.exec(str); ...
- window10 java 环境变量配置
JAVA_HOME (可有可无) CLASSPATH : C:\Program Files\Java\jdk1.8.0_91\lib\dt.jar;C:\Program Files\Java\j ...
- redis07-----Redis持久化配置
Redis持久化配置 持久化: 即把数据存储于断电后不会丢失的设备中,通常是硬盘. 常见的持久化方式: 主从:通过从服务器保存和持久化,如mongoDB的replication sets配置. 淘宝是 ...
- css 中的伪类选择器before 与after
.cf:after,.cf:before {content: " "; display: table;} .cf:after {clear: both;} :before是因为ta ...
- [Android6.0][RK3399] 修改默认按键 KEY-PAD 的功能【转】
本文转载自:http://m.blog.csdn.net/dearsq/article/details/70175637 Platform: RK3399 OS: Android 6.0 Kernel ...
- Android设备adb授权的原理【转】
本文转载自:http://blog.csdn.net/zahuopuboss/article/details/50831171 http://blog.csdn.net/sowhat_ah/artic ...
- Hihocoder #1014 : Trie树 (字典数树统计前缀的出现次数 *【模板】 基于指针结构体实现 )
#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助, ...