Description

眼看着假期就要到了,Crash由于长期切题而感到无聊了,因此他决定利用这个假期和好友陶陶一起出去旅游。

Crash和陶陶所要去的城市里有N (N > 1) 个景点,Crash用正整数1到N给景点标号。

这些景点之间通过N - 1条无向道路相连,每条道路有一个长度,并且保证任意两个景点之间都有且仅有一条路径相连。

现在对于一个景点s,Crash和陶陶从s出发,然后访问一个景点序列{v0, v1, v2, … , vk},

其中v0就是s,且vi-1和vi(0 < i ≤ k)之间有道路相连。

需要注意的是,陶陶和Crash访问的景点序列中不会只有景点s。

为了使旅程不显得乏味,在一个景点序列里他们不会重复走某条道路。

我们定义这个序列的旅游代价为经过道路的长度和。下面问题出现了:

陶陶:我们走一条景点数最多的景点序列吧。

Crash:倒,你想把我累死啊。

陶陶:谁叫你整天坐在电脑前面,不出来锻炼,这下子傻了吧,哈哈哈哈~~

Crash:不行,如果你非要走景点数最多的我就不陪你走了。

陶陶:笑喷油你很跳嘛!

Crash:这样吧,我们来写伸展树,如果我写的比你快,你就要听我的。

陶陶:这样不公平哎,我们来玩PES吧,当然你要让我选法国队,如果你输了你就要听我的。

Crash:倒,你这是欺负我,T_T~

陶陶:笑喷油好说话哎。

Crash:囧……

……

这样搞了半天,最终陶陶和Crash用很多次包剪锤决定出选择旅游代价第K小 的景点序列。

不过呢Crash和陶陶还没确定开始旅行的景点s,因此他希望你对于每个景点i,计算从景点i开始的景点序列中旅游代价第K小的值。

Input

共N行。

第1行包含一个字符和两个正整数,字符为ABCD中的一个,用来表示这个测试数据的类型

(详见下面的数据规模和约定),另外两个正整数分别表示N和K (K < N),N<=100000

第2行至第N行,每行有三个正整数u、v和w (u, v ≤ N,w ≤ 10000)。

表示u号景点和v号景点之间有一条道路,长度为w。

输入文件保证符合题目的约定,即任意两个景点之间都有且仅有一条路径相连。

Output

共N行,每行有一个正整数。

第i行的正整数表示从i号景点开始的景点序列中旅游代价第K小的代价。

Sample Input

A 6 3

1 2 2

1 3 4

1 4 3

3 5 1

3 6 2

Sample Output

4

6

4

7

5

6

//样例1中输出对应的景点序列分别为:

1号景点是{1, 3},2号景点是{2, 1, 3},3号景点是{3, 1},4号景点是{4, 1, 3},5号景点是{5, 3, 1},6号景点是{6, 3, 1}。

保证每个景点到1号景点需要经过的道路数不超过30


题解

题意就是给出\(n\)个点的一棵树,对于每个点求出距离ta第\(k\)小的距离

动态点分治

我们可以先建出点分树

然后二分一个距离\(k\),判断其他点到这个点的距离小于等于这个值的有几个

那么现在的问题就是给出一个点,求出到这个点的距离不大于\(k\)的有几个点

记\(v1[u]\)数组表示\(u\)的子树内的点到\(u\)的距离,\(v2[u]\)表示\(u\)的子树内的点到\(fa[u]\)的距离

然后每次查询点\(u\)的时候就顺着点分树往上跳,期间查询所有的点到点\(u\)的距离不超过\(k\)的点

注意每次往上跳到\(fa[u]\)再查\(fa[u]\)的子树的时候会把已经统计过的\(u\)的子树的信息重复统计一部分,所以我们要容斥掉\(u\)的子树内的信息

注意往上跳的时候如果跳到点\(x\)使得\(dis(u,x)>k\)也不要直接\(break\),而是要继续往上跳,因为点分树是将原树重构了,可能\(dis(u,x)>k\)了但是\(x\)的祖先到\(u\)的距离\(\le k\)

代码

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 200005 ;
const int INF = 1e9 ;
using namespace std ; inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
} bool vis[M] ;
int n , k , num , tot , tmx , root , hea[M] ;
int dis[M] , ff[M] , fa[M] , size[M] , dep[M] , son[M] , top[M] ;
vector < int > v1[M] , v2[M] ;
struct E { int nxt , to , dis ; } edge[M * 2] ;
inline void add_edge(int from , int to , int dis) {
edge[++num].nxt = hea[from] ; edge[num].to = to ;
edge[num].dis = dis ; hea[from] = num ;
} void dfs1(int u , int father , int depth) {
ff[u] = father ; size[u] = 1 ; dep[u] = depth ; int mx = -1 ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ; if(v == father) continue ;
dis[v] = dis[u] + edge[i].dis ; dfs1(v , u , depth + 1) ;
size[u] += size[v] ; if(size[v] > mx) mx = size[v] , son[u] = v ;
}
}
void dfs2(int u , int topf) {
top[u] = topf ; if(!son[u]) return ; dfs2(son[u] , topf) ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ; if(v == ff[u] || v == son[u]) continue ;
dfs2(v , v) ;
}
}
inline int LCA(int x , int y) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x , y) ;
x = ff[top[x]] ;
}
return dep[x] < dep[y] ? x : y ;
}
inline int Gdis(int x , int y) {
return dis[x] + dis[y] - dis[LCA(x , y)] * 2 ;
}
void findroot(int u , int father) {
size[u] = 1 ; int mx = 0 ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ; if(vis[v] || v == father) continue ;
findroot(v , u) ; mx = max(mx , size[v]) ; size[u] += size[v] ;
}
mx = max(mx , tot - size[u]) ;
if(mx < tmx) tmx = mx , root = u ;
} void Solve(int u , int father) {
fa[u] = father ; vis[u] = true ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ; if(vis[v]) continue ;
tot = size[v] ; tmx = INF ;
findroot(v , u) ; Solve(root , u) ;
}
}
// v1 表示u的子树内的点到u的距离
// v2 表示u的子树内的点到fa[u]的距离
inline void Insert(int x) {
v1[x].push_back(0) ;
for(int u = x ; fa[u] ; u = fa[u]) {
v1[fa[u]].push_back(Gdis(x , fa[u])) ;
v2[u].push_back(Gdis(x , fa[u])) ;
}
}
inline int query(int id , int x , int k) {
if(id == 1) {
int l = 0 , r = v1[x].size() - 1 , mid , ret = -1 ;
while(l <= r) {
mid = (l + r) >> 1 ;
if(v1[x][mid] <= k) l = mid + 1 , ret = mid ;
else r = mid - 1 ;
}
return ret + 1 ;
}
else {
int l = 0 , r = v2[x].size() - 1 , mid , ret = -1 ;
while(l <= r) {
mid = (l + r) >> 1 ;
if(v2[x][mid] <= k) l = mid + 1 , ret = mid ;
else r = mid - 1 ;
}
return ret + 1 ;
}
}
inline int check(int x , int k) {
int ans = query(1 , x , k) ;
for(int u = x , dis1 ; fa[u] ; u = fa[u]) {
dis1 = Gdis(x , fa[u]) ; if(k - dis1 < 0) continue ;
ans += query(1 , fa[u] , k - dis1) - query(2 , u , k - dis1) ;
}
return ans - 1 ;
}
int main() {
char s[10] ; scanf("%s",s) ; n = read() ; k = read() ;
for(int i = 1 , u , v , w ; i < n ; i ++) {
u = read() ; v = read() ; w = read() ;
add_edge(u , v , w) ; add_edge(v , u , w) ;
}
dfs1(1 , 0 , 1) ; dfs2(1 , 1) ;
tot = n ; tmx = INF ; findroot(1 , 0) ; Solve(root , 0) ;
for(int i = 1 ; i <= n ; i ++) Insert(i) ;
for(int i = 1 ; i <= n ; i ++) {
sort(v1[i].begin() , v1[i].end()) ;
sort(v2[i].begin() , v2[i].end()) ;
}
for(int u = 1 ; u <= n ; u ++) {
int l = 1 , r = 1e9 , ret = 0 ;
while(l <= r) {
int mid = (l + r) >> 1 ;
if(check(u , mid) >= k) ret = mid , r = mid - 1 ;
else l = mid + 1 ;
}
printf("%d\n",ret) ;
}
return 0 ;
}

[2010国家集训队]Crash的旅游计划的更多相关文章

  1. 【BZOJ2117】 [2010国家集训队]Crash的旅游计划

    [BZOJ2117] [2010国家集训队]Crash的旅游计划 Description 眼看着假期就要到了,Crash由于长期切题而感到无聊了,因此他决定利用这个假期和好友陶陶一起出去旅游. Cra ...

  2. BZOJ2117: [2010国家集训队]Crash的旅游计划

    裸点分,点分树每层维护有序表,查询二分,复杂度$O(nlog^3n)$. #include<bits/stdc++.h> #define M (u+v>>1) #define ...

  3. BZOJ4317Atm的树&BZOJ2051A Problem For Fun&BZOJ2117[2010国家集训队]Crash的旅游计划——二分答案+动态点分治(点分树套线段树/点分树+vector)

    题目描述 Atm有一段时间在虐qtree的题目,于是,他满脑子都是tree,tree,tree…… 于是,一天晚上他梦到自己被关在了一个有根树中,每条路径都有边权,一个神秘的声音告诉他,每个点到其他的 ...

  4. BZOJ 2117: [2010国家集训队]Crash的旅游计划 动态点分治+二分

    感觉现在写点分治可快了~ 二分答案,就可以将求第 $k$ 大转换成一个判断问题,直接拿点分树判断一下就行了. #include <cstdio> #include <vector&g ...

  5. [BZOJ2051]A Problem For Fun/[BZOJ2117]Crash的旅游计划/[BZOJ4317]Atm的树

    [BZOJ2051]A Problem For Fun/[BZOJ2117]Crash的旅游计划/[BZOJ4317]Atm的树 题目大意: 给出一个\(n(n\le10^5)\)个结点的树,每条边有 ...

  6. [国家集训队] Crash 的文明世界(第二类斯特林数)

    题目 [国家集训队] Crash 的文明世界 前置 斯特林数\(\Longrightarrow\)斯特林数及反演总结 做法 \[\begin{aligned} ans_x&=\sum\limi ...

  7. 洛谷 P1829 [国家集训队]Crash的数字表格 / JZPTAB 解题报告

    [国家集训队]Crash的数字表格 / JZPTAB 题意 求\(\sum\limits_{i=1}^n\sum\limits_{j=1}^mlcm(i,j)\),\(n,m\le 10^7\) 鉴于 ...

  8. [Luogu P1829] [国家集训队]Crash的数字表格 / JZPTAB (莫比乌斯反演)

    题面 传送门:洛咕 Solution 调到自闭,我好菜啊 为了方便讨论,以下式子\(m>=n\) 为了方便书写,以下式子中的除号均为向下取整 我们来颓柿子吧qwq 显然,题目让我们求: \(\l ...

  9. 题解-[国家集训队]Crash的数字表格 / JZPTAB

    题解-[国家集训队]Crash的数字表格 / JZPTAB 前置知识: 莫比乌斯反演 </> [国家集训队]Crash的数字表格 / JZPTAB 单组测试数据,给定 \(n,m\) ,求 ...

随机推荐

  1. 关于MSSQL的decimal(numeric)、money、float的使用以及区别

    decimal(numeric).money.float(real) 都是MSSQL中的浮点类型的数据类型. 按存储的范围进行排序 float(real) decimal(numeric) money ...

  2. 暴力破解unix/linux平台上采用crypt加密的口令

    # coding=utf-8 ''' 暴力破解crypt模块加密的密码 ''' import crypt import optparse usage = 'Usage: %prog [optinos] ...

  3. Eclipse+Maven(webapp)+Jetty+JReBel的配置方法

    maven配置 省略 jrebel配置 jrebel毋须繁琐的配置,把jrebel-5.6.3-crack.zip解压放在磁盘文件夹就可以.(笔者路径为:D:\coding-life\IDE\jreb ...

  4. iOS开发——高级篇——iOS涂鸦画板效果实现

    一个简单的绘图应用,模仿苹果自带软件备忘录里的涂鸦功能 核心代码 #import "DrawView.h" #import "DrawPath.h" @inte ...

  5. SSH常见错误

    错误一: IOException parsing XML document from ServletContext resource [/WEB-INF/applicationContext.xml] ...

  6. Codeforces Round #261 (Div. 2)——Pashmak and Graph

    题目链接 题意: n个点.m个边的有向图.每条边有一个权值,求一条最长的路径,使得路径上边值严格递增.输出路径长度 )) 分析: 由于路径上会有反复点,而边不会反复.所以最開始想的是以边为状态进行DP ...

  7. js加减乘除丢失精度

    js加减乘除(学了那么久现在才注意到汗==!) /** ** 除法函数,用来得到精确的除法结果 ** 说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显.这个函数返回较为精 ...

  8. casperjs在拆分文件后的中文乱码问题的解决

    windows环境. capserjs的中文乱码使用phantom.outputEncoding="GBK";即可解决. 但当我们脚本很大,需要拆分时(参考http://docs. ...

  9. YTU 1099: Minesweeper

    1099: Minesweeper 时间限制: 1 Sec  内存限制: 64 MB 提交: 180  解决: 98 题目描述 Minesweeper Have you ever played Min ...

  10. Ubuntu18开启redis服务自启动

    设置redis服务开机自启动. 1.创建配置文件夹 sudo mkdir /etc/redis sudo cp /usr/local/redis/redis.conf /etc/redis sudo ...