bzoj2051. A Problem For Fun 【动态点分治】+【动态开点权值线段树】

6 篇文章 0 订阅
4 篇文章 0 订阅

传送门
给出一个 N N N个结点的树,每条边有一个正整数权值,定义两个结点的距离为连接这两个结点路径上边权的和。对于每个结点 i i i,它到其他 N − 1 N-1 N1个结点都有一个距离,将这些距离从小到大排序,输出第 K K K个距离。
三倍经验
bzoj2117. [2010国家集训队]Crash的旅游计划
bzoj4317. Atm的树
(后面有这两题的代码)

分析

  1. 对于某个点到其它的点的所有距离可以通过,动态点分治来进行查询
  2. 对于第 K K K大,通常通过数据结构维护,或者直接暴力二分

动态点分治

这里有篇介绍【动态淀粉质】的详解,可以看一下思想
对于每个点,我们需要存储当前节点管辖的区间,该节点到所有点的距离
维护这个距离,且维护的数据能够在 l o g log log左右的时间复杂度内查询类似于第 K K K大或者比 x x x大的个数的操作,支持这种操作的可以用【数组(vector)】【权值线段树】【平衡树】等
注意,数组来写或许会更方便。这里采用权值线段树维护(动态开点),主要是练习一下使用动态开点。

对于当前区间维护的所有点的距离维护好了,我们查询的时候是往上跳父节点,这里需要简单去重容斥一下。
因为:我们在父节点找数据的时候,会囊括当前节点管辖的区间,需要去重(消去当前节点已经计算过的对父节点的影响)。

二分答案

在动态点分治完成后,对于每一个节点,对第 K K K大进行二分
假设当前二分的答案为 x x x
我们在动态点分树上进行查找,所有小于等于 x x x的距离个数,记为 c n t cnt cnt
如果 c n t > = K cnt>=K cnt>=K说明正确的答案更小,反之同理

代码

//bz2051 
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("1.in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 2e6+5;
const ll MOD = 1e9+7;
int N, M, K;

struct Tr {
	int k, l, r;
}tr[MAX_N<<4];
int indx = 0;

int mk() {
	return ++indx;
}

void push_up(int rt) {
	tr[rt].k = tr[tr[rt].l].k + tr[tr[rt].r].k;
}

void update(int& rt, int l, int r, int x) {
	if (!rt) rt = mk();
	if (l == r) {
		tr[rt].k += 1;
		return;
	}
	int mid = l + ((r-l)>>1);
	if (x <= mid) update(tr[rt].l, l, mid, x);
	if (x  > mid) update(tr[rt].r, mid+1, r, x);
	push_up(rt);
}

int query(int rt, int l, int r, int x, int y) {
	if (!rt) return 0;
	if (x <= l && r <= y) {
		return tr[rt].k;
	}
	int mid = l + ((r-l)>>1);
	if (y <= mid) return query(tr[rt].l, l, mid, x, y);
	if (x  > mid) return query(tr[rt].r, mid+1, r, x , y);
	return query(tr[rt].l, l, mid, x, y) + query(tr[rt].r, mid+1, r, x, y);
}

int head[MAX_N];
int tot = 0;
struct Edge {
	int to, nxt, w;
}edge[MAX_N];

void addEdge(int u, int v, int w) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	edge[tot].w = w;
	head[u] = tot++;
	edge[tot].nxt = head[v];
	edge[tot].to = u;
	edge[tot].w = w;
	head[v] = tot++;
}

int dep[MAX_N];
int len[MAX_N];
int top[MAX_N];
int son[MAX_N];
int sz[MAX_N];
int parent[MAX_N];

void dfs1(int u, int from, int k) {
	parent[u] = from;
	len[u] = k;
	sz[u] = 1;
	son[u] = 0;
	dep[u] = dep[from] + 1;
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from) continue;
		dfs1(v, u, k + edge[i].w);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) {
			son[u] = v;
		}
	}
}

void dfs2(int u, int tp) {
	top[u] = tp;
	if (son[u]) dfs2(son[u], tp);
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == son[u] || v == parent[u]) continue;
		dfs2(v, v);
	}
}

int LCA(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) swap(x, y);
		x = parent[top[x]];
	}
	return dep[x] < dep[y] ? x : y;
}

int dis(int x, int y) {
	int lca = LCA(x, y);
	return len[x] + len[y] - 2 * len[lca];
}

bool vis[MAX_N];
int maxx[MAX_N];
int father[MAX_N];

void dfs(int u, int from, int& rt, int sum) {
	sz[u] = 1;
	maxx[u]  = 0;
	int v;
	
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from || vis[v]) continue;
		dfs(v, u, rt, sum);
		sz[u] += sz[v];
		maxx[u] = max(maxx[u], sz[v]);
	}
	maxx[u] = max(maxx[u], sum - sz[u]);
	if (maxx[u] < maxx[rt]) {
		rt = u;
	}
}

int A[MAX_N];
int B[MAX_N];
int range[MAX_N];

void get_ra(int rt, int u, int from, int w) {
	range[rt] = max(range[rt], w);
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from || vis[v]) continue;
		get_ra(rt, v, u, w + edge[i].w);
	}
}

void divide(int u, int sum) {
	range[u] = 0;
	get_ra(u, u, 0, 0);
	vis[u] = 1;
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if (vis[v=edge[i].to]) continue;
		int rt = 0;
		dfs(v, u, rt, sz[v]);
		father[rt] = u;
		divide(rt, sz[v]);
	}
}

int count(int x, int limit) {
	int res = 0, pre = 0, d = 0;
	for (int i = x; i; pre = i, i = father[i]) {
		if ((d = dis(x, i)) > limit) continue;
		res += query(A[i], 1, range[i] + 1, 1, limit - d + 1);
		if (pre) res -= query(B[pre], 1, range[i] + 1, 1, limit - d + 1);
	}
	return res;
}

void modify(int x) {
	int d;
	for (int i = x; i; i = father[i]) {
		update(A[i], 1, range[i] + 1, dis(x, i) + 1);
		if (father[i]) update(B[i], 1, range[father[i]] + 1, dis(x, father[i]) + 1);
	}
}

int calc(int x) {
	int l = 0, r = 1e9, mid = 0, ans = 0;
	while (l <= r) {
		mid = l + ((r-l)>>1);
		if (count(x, mid) >= K) {
			ans = mid;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	return ans;
}

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

int arr[MAX_N];

void solve(){
	init();
	sc("%d%d", &N, &K);
	int u, v, w;
	
	for (int i = 1; i < N; ++i) {
		sc("%d%d%d", &u, &v, &w);
		addEdge(u, v, w);
	}
	
	dfs1(1, 0, 0);
	dfs2(1, 1);
	int rt = 0;
	maxx[rt] = 1e10;
	dfs(1, 0, rt, N);
	divide(rt, N);
	
	for (int i = 1; i <= N; ++i) {
		modify(i);
	}
	
	++K;
	
	for (int i = 1; i <= N; ++i) {
		pr("%d\n", calc(i));
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
	FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}


bzoj2117 [2010国家集训队]Crash的旅游计划

//bz2051 
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("1.in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 2e6+5;
const ll MOD = 1e9+7;
int N, M, K;

struct Tr {
	int k, l, r;
}tr[MAX_N*30];
int indx = 0;

int mk() {
	if (indx > (MAX_N*30) - 10) {
		pr("%d", indx);
		exit(0);
	}
	return ++indx;
}

void push_up(int rt) {
	tr[rt].k = tr[tr[rt].l].k + tr[tr[rt].r].k;
}

void update(int& rt, int l, int r, int x) {
	if (!rt) rt = mk();
	if (l == r) {
		tr[rt].k += 1;
		return;
	}
	int mid = l + ((r-l)>>1);
	if (x <= mid) update(tr[rt].l, l, mid, x);
	if (x  > mid) update(tr[rt].r, mid+1, r, x);
	push_up(rt);
}

int query(int rt, int l, int r, int x, int y) {
	if (!rt) return 0;
	if (x <= l && r <= y) {
		return tr[rt].k;
	}
	int mid = l + ((r-l)>>1);
	if (y <= mid) return query(tr[rt].l, l, mid, x, y);
	if (x  > mid) return query(tr[rt].r, mid+1, r, x , y);
	return query(tr[rt].l, l, mid, x, y) + query(tr[rt].r, mid+1, r, x, y);
}

int head[MAX_N];
int tot = 0;
struct Edge {
	int to, nxt, w;
}edge[MAX_N];

void addEdge(int u, int v, int w) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	edge[tot].w = w;
	head[u] = tot++;
	edge[tot].nxt = head[v];
	edge[tot].to = u;
	edge[tot].w = w;
	head[v] = tot++;
}

int dep[MAX_N];
int len[MAX_N];
int top[MAX_N];
int son[MAX_N];
int sz[MAX_N];
int parent[MAX_N];

void dfs1(int u, int from, int k) {
	parent[u] = from;
	len[u] = k;
	sz[u] = 1;
	son[u] = 0;
	dep[u] = dep[from] + 1;
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from) continue;
		dfs1(v, u, k + edge[i].w);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) {
			son[u] = v;
		}
	}
}

void dfs2(int u, int tp) {
	top[u] = tp;
	if (son[u]) dfs2(son[u], tp);
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == son[u] || v == parent[u]) continue;
		dfs2(v, v);
	}
}

int LCA(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) swap(x, y);
		x = parent[top[x]];
	}
	return dep[x] < dep[y] ? x : y;
}

int dis(int x, int y) {
	int lca = LCA(x, y);
	return len[x] + len[y] - 2 * len[lca];
}

bool vis[MAX_N];
int maxx[MAX_N];
int father[MAX_N];

void dfs(int u, int from, int& rt, int sum) {
	sz[u] = 1;
	maxx[u]  = 0;
	int v;
	
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from || vis[v]) continue;
		dfs(v, u, rt, sum);
		sz[u] += sz[v];
		maxx[u] = max(maxx[u], sz[v]);
	}
	maxx[u] = max(maxx[u], sum - sz[u]);
	if (maxx[u] < maxx[rt]) {
		rt = u;
	}
}

int A[MAX_N];
int B[MAX_N];
int range[MAX_N];

void get_ra(int rt, int u, int from, int w) {
	range[rt] = max(range[rt], w);
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from || vis[v]) continue;
		get_ra(rt, v, u, w + edge[i].w);
	}
}

void divide(int u, int sum) {
	range[u] = 0;
	get_ra(u, u, 0, 0);
	vis[u] = 1;
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if (vis[v=edge[i].to]) continue;
		int rt = 0;
		int vsz = sz[u] > sz[v] ? sz[v] : sum - sz[u];
		dfs(v, u, rt, vsz);
		father[rt] = u;
		divide(rt, vsz);
	}
}

int count(int x, int limit) {
	int res = 0, pre = 0, d = 0;
	for (int i = x; i; pre = i, i = father[i]) {
		if ((d = dis(x, i)) > limit) continue;
		res += query(A[i], 1, range[i] + 1, 1, limit - d + 1);
		if (pre) res -= query(B[pre], 1, range[i] + 1, 1, limit - d + 1);
	}
	return res;
}

void modify(int x) {
	int d;
	for (int i = x; i; i = father[i]) {
		update(A[i], 1, range[i] + 1, dis(x, i) + 1);
		if (father[i]) update(B[i], 1, range[father[i]] + 1, dis(x, father[i]) + 1);
	}
}

int calc(int x) {
	int l = 0, r = 1e9, mid = 0, ans = 0;
	while (l <= r) {
		mid = l + ((r-l)>>1);
		if (count(x, mid) >= K) {
			ans = mid;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	return ans;
}

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

int arr[MAX_N];

void solve(){
	init();
	getchar();
	sc("%d%d", &N, &K);
	int u, v, w;
	
	for (int i = 1; i < N; ++i) {
		sc("%d%d%d", &u, &v, &w);
		addEdge(u, v, w);
	}
	
	dfs1(1, 0, 0);
	dfs2(1, 1);
	int rt = 0;
	maxx[rt] = 1e9;
	dfs(1, 0, rt, N);
	divide(rt, N);
	
	for (int i = 1; i <= N; ++i) {
		modify(i);
	}
	
	++K;
	
	for (int i = 1; i <= N; ++i) {
		pr("%d\n", calc(i));
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
//	FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}

bzoj4317Atm的树

//bz2051 
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("1.in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 2e6+5;
const ll MOD = 1e9+7;
int N, M, K;

struct Tr {
	int k, l, r;
}tr[MAX_N<<4];
int indx = 0;

int mk() {
	return ++indx;
}

void push_up(int rt) {
	tr[rt].k = tr[tr[rt].l].k + tr[tr[rt].r].k;
}

void update(int& rt, int l, int r, int x) {
	if (!rt) rt = mk();
	if (l == r) {
		tr[rt].k += 1;
		return;
	}
	int mid = l + ((r-l)>>1);
	if (x <= mid) update(tr[rt].l, l, mid, x);
	if (x  > mid) update(tr[rt].r, mid+1, r, x);
	push_up(rt);
}

int query(int rt, int l, int r, int x, int y) {
	if (!rt) return 0;
	if (x <= l && r <= y) {
		return tr[rt].k;
	}
	int mid = l + ((r-l)>>1);
	if (y <= mid) return query(tr[rt].l, l, mid, x, y);
	if (x  > mid) return query(tr[rt].r, mid+1, r, x , y);
	return query(tr[rt].l, l, mid, x, y) + query(tr[rt].r, mid+1, r, x, y);
}

int head[MAX_N];
int tot = 0;
struct Edge {
	int to, nxt, w;
}edge[MAX_N];

void addEdge(int u, int v, int w) {
	edge[tot].nxt = head[u];
	edge[tot].to = v;
	edge[tot].w = w;
	head[u] = tot++;
	edge[tot].nxt = head[v];
	edge[tot].to = u;
	edge[tot].w = w;
	head[v] = tot++;
}

int dep[MAX_N];
int len[MAX_N];
int top[MAX_N];
int son[MAX_N];
int sz[MAX_N];
int parent[MAX_N];

void dfs1(int u, int from, int k) {
	parent[u] = from;
	len[u] = k;
	sz[u] = 1;
	son[u] = 0;
	dep[u] = dep[from] + 1;
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from) continue;
		dfs1(v, u, k + edge[i].w);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) {
			son[u] = v;
		}
	}
}

void dfs2(int u, int tp) {
	top[u] = tp;
	if (son[u]) dfs2(son[u], tp);
	
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == son[u] || v == parent[u]) continue;
		dfs2(v, v);
	}
}

int LCA(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) swap(x, y);
		x = parent[top[x]];
	}
	return dep[x] < dep[y] ? x : y;
}

int dis(int x, int y) {
	int lca = LCA(x, y);
	return len[x] + len[y] - 2 * len[lca];
}

bool vis[MAX_N];
int maxx[MAX_N];
int father[MAX_N];

void dfs(int u, int from, int& rt, int sum) {
	sz[u] = 1;
	maxx[u]  = 0;
	int v;
	
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from || vis[v]) continue;
		dfs(v, u, rt, sum);
		sz[u] += sz[v];
		maxx[u] = max(maxx[u], sz[v]);
	}
	maxx[u] = max(maxx[u], sum - sz[u]);
	if (maxx[u] < maxx[rt]) {
		rt = u;
	}
}

int A[MAX_N];
int B[MAX_N];
int range[MAX_N];

void get_ra(int rt, int u, int from, int w) {
	range[rt] = max(range[rt], w);
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if ((v=edge[i].to) == from || vis[v]) continue;
		get_ra(rt, v, u, w + edge[i].w);
	}
}

void divide(int u, int sum) {
	range[u] = 0;
	get_ra(u, u, 0, 0);
	vis[u] = 1;
	int v;
	for (int i = head[u];~i;i=edge[i].nxt) {
		if (vis[v=edge[i].to]) continue;
		int rt = 0;
		dfs(v, u, rt, sz[v]);
		father[rt] = u;
		divide(rt, sz[v]);
	}
}

int count(int x, int limit) {
	int res = 0, pre = 0, d = 0;
	for (int i = x; i; pre = i, i = father[i]) {
		if ((d = dis(x, i)) > limit) continue;
		res += query(A[i], 1, range[i] + 1, 1, limit - d + 1);
		if (pre) res -= query(B[pre], 1, range[i] + 1, 1, limit - d + 1);
	}
	return res;
}

void modify(int x) {
	int d;
	for (int i = x; i; i = father[i]) {
		update(A[i], 1, range[i] + 1, dis(x, i) + 1);
		if (father[i]) update(B[i], 1, range[father[i]] + 1, dis(x, father[i]) + 1);
	}
}

int calc(int x) {
	int l = 0, r = 1e9, mid = 0, ans = 0;
	while (l <= r) {
		mid = l + ((r-l)>>1);
		if (count(x, mid) >= K) {
			ans = mid;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	return ans;
}

void init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

int arr[MAX_N];

void solve(){
	init();
	sc("%d%d", &N, &K);
	int u, v, w;
	
	for (int i = 1; i < N; ++i) {
		sc("%d%d%d", &u, &v, &w);
		addEdge(u, v, w);
	}
	
	dfs1(1, 0, 0);
	dfs2(1, 1);
	int rt = 0;
	maxx[rt] = 1e9;
	dfs(1, 0, rt, N);
	divide(rt, N);
	
	for (int i = 1; i <= N; ++i) {
		modify(i);
	}
	
	++K;
	
	for (int i = 1; i <= N; ++i) {
		pr("%d\n", calc(i));
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
//	FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (T--) solve();

	return AC;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值