CodeForces 1110 F Nearest Leaf

传送门.

翻译:

给出一棵带权树,树的dfs序是确定的。

每次询问给出x,l,r,问dfs序属于[l…r]的叶子点到x最近的距离是多少。

题解:

不难想到一个暴力的做法:
每次x往父亲跳,查询其它子树里的叶子节点。
复杂度 O ( n 2 ) O(n^2) O(n2)

可以强行套上树链剖分搞这个,时间复杂度为 ( n l o g 2 n ) (n log^2n) (nlog2n),空间复杂度通过离线询问可以达到 O ( n l o g n ) O(nlogn) O(nlogn)

发现根本没有用到是要求dfs序在[l…r]间的性质,这分明是任意序列都行。

dfs序的性质就是一个子树的在连续一段。

仔细想想乱搞,发现从上往下走,记录f[i]表示dfs序为i的点到当前点的距离。

加入要从x->y cost = z,那么其实就是y的子树里的距离-z,子树外的距离+z

那么用线段树维护区间加和区间最小值即可。

Code:

#include<cstdio>
#include<vector>
#include<cstring>
#define pp printf
#define ll long long
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
using namespace std;

const int N = 5e5 + 5;

int n, Q, x;
int fa[N], fv[N];

vector<int> c[N];


struct nod {
	int i, l, r;
} b;

vector<nod> a[N];


int pl, pr; ll px;
ll t[N * 4], lz[N * 4];

#define i0 i + i
#define i1 i + i + 1
#define max(a, b) ((a) > (b) ? (a) : (b))
void down(int i) {
	if(lz[i]) {
		t[i0] += lz[i]; lz[i0] += lz[i]; 
		t[i1] += lz[i]; lz[i1] += lz[i];
		lz[i] = 0;
	}
}
void add(int i, int x, int y) {
	if(y < pl || x > pr) return;
	if(x >= pl && y <= pr) { t[i] += px; lz[i] += px; return;}
	int m = x + y >> 1; down(i);
	add(i0, x, m); add(i1, m + 1, y);
	t[i] = min(t[i0], t[i1]);
}
void ft(int i, int x, int y) {
	if(y < pl || x > pr) return ;
	if(x >= pl && y <= pr) { px = min(px, t[i]); return ;}
	int m = x + y >> 1; down(i);
	ft(i0, x, m); ft(i1, m + 1, y);
}

int p[N], q[N], tp;
ll s[N];

const ll M = 1e18;

void dg(int x) {
	p[x] = ++ tp;
	fo(j, 0, c[x].size() - 1) {
		int y = c[x][j];
		s[y] = s[x] + fv[y], dg(y);
	}
	q[x] = tp;
	if(p[x] == q[x]) {
		pl = pr = p[x]; px = s[x] - M;
		add(1, 1, n);
	}
}

ll ans[N];

void dfs(int x) {
	fo(j, 0, a[x].size() - 1) {
		pl = a[x][j].l, pr = a[x][j].r; px = 1e18;
		ft(1, 1, n);
		ans[a[x][j].i] = px;
	}
	fo(j, 0, c[x].size() - 1) {
		int y = c[x][j];
		px = -fv[y];
		pl = p[y]; pr = q[y];
		add(1, 1, n);
		px = fv[y];
		pl = 1; pr = p[y] - 1;
		add(1, 1, n);
		pl = q[y] + 1; pr = n;
		add(1, 1, n);
		
		dfs(y);
		
		px = -fv[y];
		pl = q[y] + 1; pr = n;
		add(1, 1, n);
		pl = 1; pr = p[y] - 1;
		add(1, 1, n);
		px = fv[y];
		pl = p[y]; pr = q[y];
		add(1, 1, n);
	}
}
int main() {
//	freopen("a.in", "r", stdin);
	scanf("%d %d", &n, &Q);
	fo(i, 1, n * 4) t[i] = 1e18;
	fo(i, 2, n) {
		scanf("%d %d", &fa[i], &fv[i]);
		c[fa[i]].push_back(i);
	}
	fo(i, 1, Q) {
		scanf("%d", &x);
		scanf("%d %d", &b.l, &b.r); b.i = i;
		a[x].push_back(b);
	}
	dg(1); dfs(1);
	fo(i, 1, Q) pp("%I64d\n", ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值