分为两种类型:
第一种是对点的,第二种是对边的。
对点的思路就是设一个差分数组f[i],表示对从节点i到根节点所有点的操作,包括两个端点。然后具体的操作就是:例如我们要对树上一条以l和r为端点的路径所经过的所有的点进行权值加1的操作,那么我们可以对f[l]++,f[r]++,然后f[lca(l, r)]--,f[father[lca(l,r)]]--,之后再跑一遍dfs即可。
对边的操作和对点的差不多,只不过我们的差分数组f[i]表示的是从i节点到根节点的路径上所经过的边进行的操作。然后具体的操作就是:例如我们要对l和r两点之间的路径上的每条边的边权进行加1操作,那么我们可以对f[l]++,f[r]++,然后f[lca(l,r)]-=2,再作一遍dfs即可。
详细过程可以自己根据数组的含义来推一下就知道了。
两个例题:https://www.luogu.org/problemnew/show/P2680 边的
code:
// luogu-judger-enable-o2
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3e5+10, M=3e5+10;
struct edge {
int v, w, next;
} e[N<<1];
struct query {
int from, to, lca, len;
} q[M];
struct message {
int f, l;
};
int n, m;
int cnt, head[N];
int deep[N], dis[N][30], p[N][30];
int read() {
int x=0, w=1;
char ch=0;
while (ch<'0' || ch>'9') {
if (ch=='-') w=-1;
ch=getchar();
}
while (ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*w;
}
void add(int a, int b, int c) {
e[++cnt].v=b;
e[cnt].w=c;
e[cnt].next=head[a];
head[a]=cnt;
return ;
}
void dfs(int now) {
for (int i=head[now]; i; i=e[i].next) {
int to=e[i].v, len=e[i].w;
if (to==p[now][0]) continue;
p[to][0]=now;
dis[to][0]=len;
deep[to]=deep[now]+1;
dfs(to);
}
return ;
}
void init() {
for (int j=1; (1<<j)<=n; j++) {
for (int i=1; i<=n; i++) {
p[i][j]=p[p[i][j-1]][j-1];
dis[i][j]=dis[p[i][j-1]][j-1]+dis[i][j-1];
}
}
return ;
}
message lca(int a, int b) {
if (deep[a]>deep[b]) swap(a, b);
int h=deep[b]-deep[a], way=0;
for (int i=0; (1<<i)<=h; i++)
if (h&(1<<i)) {
way+=dis[b][i];
b=p[b][i];
}
if (a!=b) {
for (int i=(int)log2(n); i>=0; i--)
if (p[a][i]!=p[b][i]) {
way+=dis[a][i]+dis[b][i];
a=p[a][i], b=p[b][i];
}
way+=dis[a][0]+dis[b][0];
a=p[a][0];
}
return message{a, way};
}
int tot, Maxw, fre[N];
void push_fre(int now) {
for (int i=head[now]; i; i=e[i].next) {
int to=e[i].v;
if (to==p[now][0]) continue;
push_fre(to);
fre[now]+=fre[to];
}
if (fre[now]==tot) Maxw=max(dis[now][0], Maxw);
return ;
}
bool check(int x) {
int Maxlen=-1;
tot=0;
memset(fre, 0, sizeof(fre));
for (int i=1; i<=m; i++)
if (q[i].len>x) tot++, fre[q[i].from]++, fre[q[i].to]++, fre[q[i].lca]-=2, Maxlen=max(Maxlen, q[i].len);
if (tot==0) return true;
Maxw=-1;
push_fre(1);
if (Maxlen-Maxw<=x) return true;
else return false;
}
int main() {
int x, y, z, l=-1, r=-1;
n=read(), m=read();
for (int i=1; i<=n-1; i++) {
x=read(), y=read(), z=read();
add(x, y, z);
add(y, x, z);
}
dfs(1);
init();
for (int i=1; i<=m; i++) {
q[i].from=read(), q[i].to=read();
message tmp=lca(q[i].from, q[i].to);
q[i].len=tmp.l, q[i].lca=tmp.f;
r=max(r, q[i].len);
}
while (r-l>1) {
int mid=(l+r)>>1;
if (check(mid)) r=mid;
else l=mid;
}
printf("%d", r);
return 0;
}
https://www.luogu.org/problemnew/show/P3258点的
code:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=3e5+10;
struct edge{
int v, next;
} e[N<<1];
int n, a[N];
int cnt, head[N];
int d[N], p[N][30];
int value[N];
void add(int a, int b) {
e[++cnt].v=b;
e[cnt].next=head[a];
head[a]=cnt;
return ;
}
void dfs(int u) {
for (int i=head[u]; i; i=e[i].next) {
int to=e[i].v;
if (to==p[u][0]) continue;
p[to][0]=u;
d[to]=d[u]+1;
dfs(to);
}
return ;
}
void init() {
for (int i=1; (1<<i)<=n; i++)
for (int j=1; j<=n; j++)
p[j][i]=p[p[j][i-1]][i-1];
return ;
}
int lca(int a, int b) {
if (d[a]>d[b]) swap(a, b);
int h=d[b]-d[a];
for (int i=0; (1<<i)<=h; i++)
if ((1<<i)&h) b=p[b][i];
if (a!=b) {
for (int i=(int)log2(n); i>=0; i--)
if (p[a][i]!=p[b][i]) a=p[a][i], b=p[b][i];
a=p[a][0];
}
return a;
}
void dfs_push(int u) {
for (int i=head[u]; i; i=e[i].next){
int to=e[i].v;
if (to==p[u][0]) continue;
dfs_push(to);
value[u]+=value[to];
}
return ;
}
int main() {
int x, y, root=1;
cin >> n;
for (int i=1; i<=n; i++)
cin >> a[i];
for (int i=1; i<=n-1; i++) {
cin >> x >> y;
add(x, y);
add(y, x);
}
dfs(root);
init();
for (int i=1; i<=n-1; i++) {
int tmp=lca(a[i], a[i+1]);
value[a[i]]++;
value[p[a[i+1]][0]]++;
value[tmp]--;
value[p[tmp][0]]--;
}
dfs_push(root);
for (int i=1; i<=n; i++)
cout << value[i] << endl;
return 0;
}