树上差分

分为两种类型:

第一种是对点的,第二种是对边的。

对点的思路就是设一个差分数组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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值