CTSC模拟题 树上的路径
Description
给定一棵\(N\)个结点的树,结点用正整数\(1 \dots N\)编号,每条边有一个正整数权值。用\(d(a, b)\)表示从结点\(a\)到结点\(b\)路径上经过边的权值和,其中要求\(a < b\)。将这\(\frac{N(N-1)}{2}\)个距离值从大到小排序,输出前\(M\)个距离值。
Input
第一行包含两个正整数\(N,M\)。
下面\(N − 1\)行,每行三个正整数\(a, b, c (a, b \le N, c \le 10,000)\),表示结点\(a\)和结点\(b\)间有一条权值为\(c\)的边相连。
Output
共\(M\)行,每行一个正整数,第\(i\)行表示排在第\(i\)个的距离值。
Sample Input
5 10
1 2 1
1 3 2
2 4 3
2 5 4
Sample Output
7
7
6
5
4
4
3
3
2
1
Hint
\(N \le 50000,M \le min(\frac{N(N-1)}{2},30,000)\)
点分治+K路归并。由于树上的每条路径必定经过一个重心,在进行点分治时我们可以枚举到所有的路径。将重心到每个点的距离按dfs序插入数组(可以把每次分治的结果都插入数组中,数组开\(O(NlogN)\))中。由于保证枚举的两个点不在一棵子数中,每个点对应的链在dfs数组中一定是段连续的区间,故可以采用超级钢琴的做法。代码如下:
#include<algorithm>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<set>
#include<vector>
using namespace std;
#define maxn (100010)
int N,M,next[maxn],toit[maxn],len[maxn],side[maxn],L,R,f[20][maxn*10],bit[maxn*10];
int cnt = 1,num,size[maxn],large[maxn],dis[maxn*10],best; bool vis[maxn];
struct node
{
int pos,l,r,mx,key;
friend inline bool operator <(const node &a,const node &b) { return a.key > b.key; }
};
vector <node> vec; multiset <node> S;
inline void add(int a,int b,int c) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; len[cnt] = c; }
inline void ins(int a,int b,int c) { add(a,b,c); add(b,a,c); }
inline void getroot(int now,int fa,int rest)
{
size[now] = 1; large[now] = 0;
for (int i = side[now];i;i = next[i])
{
if (toit[i] == fa||vis[toit[i]]) continue;
getroot(toit[i],now,rest);
size[now] += size[toit[i]];
large[now] = max(large[now],size[toit[i]]);
}
large[now] = max(large[now],rest-size[now]);
if (large[now] < large[best]) best = now;
}
inline int find_root(int now,int rest) { best = 0; getroot(now,0,rest); return best; }
inline void dfs(int now,int fa,int d)
{
dis[++cnt] = d; vec.push_back((node){cnt,L,R,0,0});
for (int i = side[now];i;i = next[i])
if (toit[i] != fa&&!vis[toit[i]])
dfs(toit[i],now,d+len[i]);
}
inline void cut(int now)
{
vis[now] = true; L = R = ++cnt;
for (int i = side[now];i;i = next[i])
{
if (vis[toit[i]]) continue;
dfs(toit[i],now,len[i]); R = cnt;
}
for (int i = side[now];i;i = next[i])
if (!vis[toit[i]]) cut(find_root(toit[i],size[toit[i]]));
}
inline int query(int l,int r)
{
int len = r-l+1,lg = bit[len];
if (dis[f[lg][l]] > dis[f[lg][r-(1<<lg)+1]]) return f[lg][l];
else return f[lg][r-(1<<lg)+1];
}
int main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%d %d",&N,&M);
for (int i = 1,a,b,c;i < N;++i) scanf("%d %d %d",&a,&b,&c),ins(a,b,c);
large[0] = 1<<30; cnt = 0; cut(find_root(1,N));
for (int i = 1;(1 << i) <= (cnt<<1);++i)
for (int j = (1<<(i-1));j <= (1<<i)-1&&j <= cnt;++j) bit[j] = i-1;
for (int i = 1;i <= cnt;++i) f[0][i] = i;
for (int i = 1;i <= bit[cnt];++i)
for (int j = 1;j+(1<<i)-1 <= cnt;++j)
{
if (dis[f[i-1][j]] > dis[f[i-1][j+(1<<(i-1))]]) f[i][j] = f[i-1][j];
else f[i][j] = f[i-1][j+(1<<(i-1))];
}
int nn = vec.size();
for (int i = 0;i < nn;++i)
{
node tmp = vec[i];
tmp.mx = query(tmp.l,tmp.r); tmp.key = dis[tmp.pos]+dis[tmp.mx];
S.insert(tmp);
}
for (int i = 1;i <= M;++i)
{
node now = *S.begin(); S.erase(S.begin());
printf("%d\n",now.key);
if (now.mx > now.l)
{
node tmp; tmp = now;
tmp.mx = query(tmp.l,tmp.mx-1);
tmp.key = dis[tmp.mx]+dis[tmp.pos];
tmp.r = now.mx-1; S.insert(tmp);
}
if (now.mx < now.r)
{
node tmp; tmp = now;
tmp.mx = query(tmp.mx+1,tmp.r);
tmp.key = dis[tmp.mx]+dis[tmp.pos];
tmp.l = now.mx+1; S.insert(tmp);
}
}
fclose(stdin); fclose(stdout);
return 0;
}
CTSC模拟题 树上的路径的更多相关文章
- 某模拟赛C题 树上路径统计 (点分治)
题意 给定一棵有n个节点的无根树,树上的每个点有一个非负整数点权.定义一条路径的价值为路径上的点权和-路径上的点权最大值. 给定参数P,我!=们想知道,有多少不同的树上简单路径,满足它的价值恰好是P的 ...
- 【BZOJ-3784】树上的路径 点分治 + ST + 堆
3784: 树上的路径 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 462 Solved: 153[Submit][Status][Discuss ...
- codevs 2756树上的路径
题意: 2756 树上的路径 时间限制: 3 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 给出一棵树,求出最小的k,使得,且在树 ...
- 全国信息学奥林匹克联赛 ( NOIP2014) 复赛 模拟题 Day1 长乐一中
题目名称 正确答案 序列问题 长途旅行 英文名称 answer sequence travel 输入文件名 answer.in sequence.in travel.in 输出文件名 answer. ...
- bzoj 3784: 树上的路径 堆维护第k大
3784: 树上的路径 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 88 Solved: 27[Submit][Status][Discuss] ...
- ACdreamoj(1105)模拟题
题意:射一次激光最多能够攻击到几个敌人(因为激光非常强大,能够在击中敌人后穿过它,而瑶瑶自己的坦克因为有特殊装置,所以不会被激光击中.激光也会直接穿过它) . 表示此处为空地 * 表示此处为障碍(激光 ...
- NOIP模拟题 2017.7.3 - 模拟 - 贪心 - 记忆化搜索
直接暴力模拟,注意判数据结构为空时的取出操作. Code #include<iostream> #include<cstdio> #include<ctime> # ...
- NOIP模拟题汇总(加厚版)
\(NOIP\)模拟题汇总(加厚版) T1 string 描述 有一个仅由 '0' 和 '1' 组成的字符串 \(A\),可以对其执行下列两个操作: 删除 \(A\)中的第一个字符: 若 \(A\)中 ...
- 【BZOJ3784】树上的路径 点分治序+ST表
[BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a< ...
随机推荐
- HDU ACM 1392 Surround the Trees->凸包
分析:直接求出凸包.再算边长就可以. 另外仅仅有一个点时为0.00单独处理,两个点直接为距离也单独处理. #include<iostream> #include<cmath> ...
- 启动MYSQL密码审计插件
http://www.innomysql.com/article/25717.html [root@server-mysql plugin]# pwd /usr/local/mysql56/lib/p ...
- WTL的消息机制
Windows消息 众所周知,Windows消息有两种:队列话消息和非队列话消息.队列话消息是被Windows操作系统放入消息队列的,程序通过主消息循环不断的从消息队列中取出消息并分发到各自的窗体调用 ...
- 基于Memcache的分布式缓存系统详解
文章不是简单的的Ctrl C与V,而是一个字一个标点符号慢慢写出来的.我认为这才是是对读者的负责,本教程由技术爱好者成笑笑(博客:http://www.chengxiaoxiao.com/)写作完成. ...
- Eclipse - 安装 run-jetty-run 插件及使用 jrebel 热部署
安装 run-jetty-run 插件 1. 下载 run-jetty-run 2. 解压至 Eclipse/MyEclipse 安装目录下的 plugin 3. 右键 web 项工程,选择 Run ...
- IIS应用地址池监控
目的:公司服务器IIS有十几个应用地址池,总在不经意间停掉一个,停止线系统日志里会有一大堆警告日志,然后就停掉了,分析了好几次,网上有人说是某一个网站的问题应该查网站, 但是网站又有那么多地址,谁知道 ...
- python基础知识四
函数是重用的程序段.它们允许你给一块语句一个名称, 然后你可以在你的程序的任何地方使用这个名称多次地运行这个语句块.这被成为调用函数.我们已经使用了许多内建的函数,比如len和range. 函数通过d ...
- 【基础】Oracle 表空间和数据文件
多个表空间的优势:1.能够将数据字典与用户数据分离出来,避免由于字典对象和用户对象保存在同一个数据文件中而产生的I/O冲突2.能够将回退数据与用户数据分离出来,避免由于硬盘损坏而导致永久性的数据丢失3 ...
- C# json与对象之间的相互转换
1. 添加命名空间 using System.Runtime.Serialization.Json; 2. WriteObject方法 // 从一个对象信息生成Json串 public static ...
- mysql 链接数据库
一.MySQL 连接本地数据库,用户名为“root”,密码“root”(注意:“-p”和“root” 之间不能有空格) C:\>mysql -h localhost -u root -proot ...