[BZOJ4317]Atm的树

试题描述

Atm有一段时间在虐qtree的题目,于是,他满脑子都是tree,tree,tree……

于是,一天晚上他梦到自己被关在了一个有根树中,每条路径都有边权,一个神秘的声音告诉他,每个点到其他的点有一个距离(什么是距离不用说吧),他需要对于每个点回答:从这个点出发的第k小距离是多少;

如果atm不能回答出来,那么明天4019的闹钟将不会响,4019全寝可能就迟到了,所以atm希望你帮帮他。

输入

第一行,两个正整数n,k,表示树的点数,询问的是第几小距离;

第二~n行,每行三个正整数x,y,w,表示x和y之间有一条边,x为父亲,边权为w;

输出

n行, 每行一个数,第i行输出从i开始第k小距离

输入示例

5 2
1 5 2
1 2 4
2 3 6
2 4 5

输出示例

4
5
10
9
6

数据规模及约定

100% n<=15000, 边权在1~10之间,为了方便,保证1为根;K<=5000

题解

参见这道题

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
	if(Head == Tail) {
		int l = fread(buffer, 1, BufferSize, stdin);
		Tail = (Head = buffer) + l;
	}
	return *Head++;
}
int read() {
	int x = 0, f = 1; char c = Getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
	return x * f;
}

#define maxn 15010
#define maxm 30010
#define maxlog 15

int n, m, head[maxn], nxt[maxm], to[maxm], dist[maxm];

void AddEdge(int a, int b, int c) {
	to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
	swap(a, b);
	to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
	return ;
}

int mnd[maxlog][maxn<<1], Log[maxn<<1], clo, dep[maxn], dfn[maxn];
void build(int u, int pa) {
	mnd[0][dfn[u] = ++clo] = dep[u];
	for(int e = head[u]; e; e = nxt[e]) if(to[e] != pa)
		dep[to[e]] = dep[u] + dist[e], build(to[e], u), mnd[0][++clo] = dep[u];
	return ;
}
void rmq_init() {
	Log[1] = 0;
	for(int i = 2; i <= clo; i++) Log[i] = Log[i>>1] + 1;
	for(int j = 1; (1 << j) <= clo; j++)
		for(int i = 1; i + (1 << j) - 1 <= clo; i++)
			mnd[j][i] = min(mnd[j-1][i], mnd[j-1][i+(1<<j-1)]);
	return ;
}
int cdist(int a, int b) {
	int l = dfn[a], r = dfn[b];
	if(l > r) swap(l, r);
	int t = Log[r-l+1];
	return dep[a] + dep[b] - (min(mnd[t][l], mnd[t][r-(1<<t)+1]) << 1);
}

#define maxnode 1800010

struct Node {
	int v, r, siz;
	Node() {}
	Node(int _, int __): v(_), r(__) {}
};
struct Treap {
	Node ns[maxnode];
	int ToT, fa[maxnode], ch[maxnode][2];

	void maintain(int o) {
		if(!o) return ;
		ns[o].siz = 1;
		for(int i = 0; i < 2; i++) if(ch[o][i])
			ns[o].siz += ns[ch[o][i]].siz;
		return ;
	}
	void rotate(int u) {
		int y = fa[u], z = fa[y], l = 0, r = 1;
		if(z) ch[z][ch[z][1]==y] = u;
		if(ch[y][1] == u) swap(l, r);
		fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
		ch[y][l] = ch[u][r]; ch[u][r] = y;
		maintain(y); maintain(u);
		return ;
	}
	void Insert(int& o, int v) {
		if(!o) {
			ns[o = ++ToT] = Node(v, rand());
			return maintain(o);
		}
		bool d = v > ns[o].v;
		Insert(ch[o][d], v); fa[ch[o][d]] = o;
		if(ns[ch[o][d]].r > ns[o].r) {
			int t = ch[o][d];
			rotate(t); o = t;
		}
		return maintain(o);
	}
	int query(int o, int v) {
		if(!o) return 0;
		int ls = ch[o][0] ? ns[ch[o][0]].siz : 0;
		if(v < ns[o].v) return query(ch[o][0], v);
		return ls + 1 + query(ch[o][1], v);
	}
} sol;

int f[maxn], siz[maxn], rt, size;
bool vis[maxn];
void getroot(int u, int pa) {
	siz[u] = 1; f[u] = 0;
	for(int e = head[u]; e; e = nxt[e]) if(to[e] != pa && !vis[to[e]]) {
		getroot(to[e], u);
		siz[u] += siz[to[e]];
		f[u] = max(f[u], siz[to[e]]);
	}
	f[u] = max(f[u], size - siz[u]);
	if(f[rt] > f[u]) rt = u;
	return ;
}
void dfs(int u, int pa) {
	siz[u] = 1;
	for(int e = head[u]; e; e = nxt[e]) if(to[e] != pa && !vis[to[e]])
		dfs(to[e], u), siz[u] += siz[to[e]];
	return ;
}
int fa[maxn];
void solve(int u) {
	vis[u] = 1;
	for(int e = head[u]; e; e = nxt[e]) if(!vis[to[e]]) {
		dfs(to[e], u);
		f[rt = 0] = size = siz[to[e]]; getroot(to[e], u);
		fa[rt] = u; solve(rt);
	}
	return ;
}

int Rt[maxn], Rtfa[maxn];
void update(int s) {
	sol.Insert(Rt[s], 0);
	for(int u = s; fa[u]; u = fa[u]) {
		int d = cdist(s, fa[u]);
//		printf("insert:: %d(fa: %d): %d\n", u, fa[u], d);
		sol.Insert(Rtfa[u], d);
		sol.Insert(Rt[fa[u]], d);
	}
	return ;
}
int query(int s, int x) {
	int ans = sol.query(Rt[s], x);
//	printf("base: %d\n", ans);
	for(int u = s; fa[u]; u = fa[u]) {
		int d = cdist(s, fa[u]);
//		printf("query:: %d(fa: %d): %d (%d - %d)\n", u, fa[u], x - d, sol.query(Rt[fa[u]], x - d), sol.query(Rtfa[u], x - d));
		ans += sol.query(Rt[fa[u]], x - d) - sol.query(Rtfa[u], x - d);
	}
	return ans;
}

int main() {
	n = read(); int K = read(), sum = 0;
	for(int i = 1; i < n; i++) {
		int a = read(), b = read(), c = read();
		AddEdge(a, b, c); sum += c;
	}

	build(1, 0);
	rmq_init();
	f[rt = 0] = size = n; getroot(1, 0);
	solve(rt);
//	for(int i = 1; i <= n; i++) printf("(%d)%d%c", i, fa[i], i < n ? ‘ ‘ : ‘\n‘);
	for(int i = 1; i <= n; i++) update(i);
	for(int i = 1; i <= n; i++) {
		int l = 1, r = sum;
		while(l < r) {
			int mid = l + r >> 1;
			if(query(i, mid) - 1 < K) l = mid + 1; else r = mid;
		}
		printf("%d\n", l);
	}

	return 0;
}

又 1A 啦 2333333!

时间: 2024-11-05 02:33:04

[BZOJ4317]Atm的树的相关文章

bzoj-2051 A Problem For Fun

题意: 给出一棵n个结点的树,边上有权值: 对于每个点求离它第k小的距离: n<=50000: 题解: 正解似乎是树分治维护距离,然后二分答案啥的,时间复杂度O(nlog^3): 但是如果想不到树分治怎么办呢?那么就来写一个逗比做法吧! 考虑从一个点转移到另外一个点,这个转移过程对于一些点是增加这条边的权值,另一些是减少这条边的权值: 而投影到DFS序上,就是对于子树区间的加减修改: 从而将原题转化成区间修改+全局K小值的问题... 呵呵 于是我去膜了膜wyfcyx大爷,得到了一个污算法.. 反

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

【树4】二叉树的遍历

简介 遍历二叉树就是按照某种顺序,将树中的结点都枚举一遍,且每个结点仅仅访问一次.因为树不是线性的结构,遍历不像线性表那样简单,因此他的遍历需要特点的算法来完成. 从某种角度讲,对二叉树的遍历就是将树形结构转换为线性结构的操作. 二叉树的遍历方法主要有如下几种: 先序遍历:先访问root结点,再先序遍历左子树,再先序遍历右子树. 中序遍历:先中序遍历左子树,再访问root结点,再中序遍历右子树. 后序遍历:先后序遍历左子树,再后序遍历右子树,再访问root结点. 层遍历:从上到下,从左到右,一层

关于左偏树的一些东东

大概所有的预备知识这里都有https://baike.baidu.com/item/%E5%B7%A6%E5%81%8F%E6%A0%91/2181887?fr=aladdin 例题1:洛谷 P3377 [模板]左偏树(可并堆) 383通过 1.2K提交 题目提供者HansBug 站长团 标签 难度提高+/省选- 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 加了路径压缩就WA,路过dal… 左偏树用指针写会MLE吗..… m,n写反了也可以过,数据有… 哪位大神有pbds库

ZJOI 2008 树的统计

ZJOI2008 树的统计 题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 输入输出格式 输入格式: 输入文件的第一行为一个整数n,表示节点的个数. 接下来n – 1行,每行2个整数

luoguP2590 [ZJOI2008]树的统计 [树链剖分] [TLE的LCT]

题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 输入输出格式 输入格式: 输入文件的第一行为一个整数n,表示节点的个数. 接下来n – 1行,每行2个整数a和b,表示节点a和节点b之

(POJ 3067) Japan (慢慢熟悉的树状数组)

Japan Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 29295   Accepted: 7902 Description Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Japan is tall island with N cities on the East coas

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include