引入
一个简单的问题。
题目描述
给你一个长度为
n
n
n 的序列
a
a
a,需要进行
m
m
m 次下面的操作。
- 将序列 a a a 中的第 l l l 个元素到第 r r r 个元素增加 x x x。
求
m
m
m 次操作后的序列
a
a
a。
数据范围
1
≤
l
≤
r
≤
n
≤
1
0
6
1\le l\le r\le n\le 10^6
1≤l≤r≤n≤106
差分
差分用于解决区间多次修改单次查询,思想就是用一个数组 d i f f diff diff 记录相邻两个数的差,每次修改 d i f f diff diff 中对应区间的值,最后查询时对数组 d i f f diff diff 数组求前缀和。
如将第 l ∼ r l\sim r l∼r 个元素增加 x x x,把 d i f f l diff_l diffl 增加 x x x,由于只修改一个区间,所以还要将 d i f f r + 1 diff_{r+1} diffr+1 减去 x x x,注意不是将 d i f f r diff_r diffr 减去 x x x,因为 r r r 也在区间范围之内。
树上差分
一般基础的线性算法涉及到树就恶心起来了。
树上差分,就是对于树上一条链或者两条链(一个点作为 LCA 的两条链拼接起来)进行多次区间修改和少数区间查询。
和线性的差分是类似的,依旧是在点上存加减值,然后从根节点一直加到两个点,但是在两点的 LCA之间要去重,原因见树上任意两点的距离。
例题
P11967 [GESP202503 八级] 割裂
树上差分的板子。
坏点对
(
x
,
y
)
(x,y)
(x,y),对于路径
x
→
l
c
a
(
x
,
y
)
x\to lca(x,y)
x→lca(x,y) 和路径
y
→
l
c
a
(
x
,
y
)
y\to lca(x,y)
y→lca(x,y),只要其中有一个点被删除,那么坏点对就不会联通。
所以,只要在删除上面两条路径中的点不会影响到好点对的联通,那么就是一个答案。
用树上差分求出好点对联通所要经过的路径就完成了。
#include<bits/stdc++.h>
//#define int long long
//#define lc p<<1
//#define rc p<<1|1
//#define lowbit(x) x&-x
#define endl putchar('\n')
#define psp putchar(' ')
using namespace std;
const int N=1e6+5;
const int M=1e3+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
void putstr(string s){
for(int i=0;i<s.size();i++)putchar(s[i]);
}
int n,m,k;
int T;
vector<int>a[N];
int dep[N];
int f[N][30];
int vis[N];
void bfs(){
queue<int>q;
q.push(1);
vis[1]=1;
dep[1]=1;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
if(vis[y])continue;
dep[y]=dep[x]+1;
f[y][0]=x;
for(int j=1;j<=20;j++)f[y][j]=f[f[y][j-1]][j-1];
vis[y]=1;
q.push(y);
}
}
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;i--)if(dep[f[x][i]]>=dep[y])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];
}
int val[N];
void dfs(int fa,int x){
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
if(y==fa)continue;
dfs(x,y);
val[x]+=val[y];
}
}
signed main(){
//ios::sync_with_stdio(0);
n=read(),m=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
a[u].push_back(v);
a[v].push_back(u);
}
bfs();
while(m--){
int u=read(),v=read();
int lca=LCA(u,v);
val[u]++;
val[v]++;
val[lca]--;
val[f[lca][0]]--;
}
dfs(0,1);
int x=read(),y=read();
int lca=LCA(x,y);
int ans=!val[lca];
while(x!=lca){
if(!val[x])ans++;
x=f[x][0];
}
while(y!=lca){
if(!val[y])ans++;
y=f[y][0];
}
print(ans);
}

被折叠的 条评论
为什么被折叠?



