@CSP模拟2019.10.16 - T3@ 垃圾分类
@description@
为了保护环境,p6pou建设了一个垃圾分类器。
垃圾分类器是一个树形结构,由 n 个垃圾桶和 n-1 条双向传送带组成。
垃圾处理器的编号为 1, 2, ..., n,每条传送带都可以花 1 秒钟将垃圾从一个垃圾桶输送到另一个垃圾桶。
垃圾投放点是编号为 r 的垃圾桶,垃圾总是投放在这里。
垃圾共有 n 种,编号也是 1, 2, ..., n。
编号为 i 的垃圾会被输送到编号为 i 的垃圾桶里面,垃圾总是自动沿着最短路线输送,到达编号为 i 的垃圾桶后需要 ai 秒才能被垃圾桶处理完成。
为了避免不同垃圾混在一起,p6pou将垃圾按照编号从小到大的顺序依次投入编号为 r 的垃圾桶,每投放一种垃圾之后,必须等到这种垃圾被输送到其他垃圾桶,或者被编号为 r 的垃圾桶处理完成,才能继续投放下一种垃圾。
传送带输送垃圾也一样,如果要将垃圾从编号为 x 的垃圾桶中的垃圾传输到编号为 y 的垃圾桶,如果此时编号为 y 的垃圾桶中有另一种垃圾,必须等到另一种垃圾被传输走或者被处理完成后才能传输。
现在p6pou想知道,开始投放垃圾到垃圾分类器处理完所有所有垃圾,总共需要花费多长时间。
输入格式
第一行两个整数 n, r,垃圾种类和垃圾桶数量都是 n,垃圾投放点是编号为 r 的垃圾桶。
第二行 n 个整数 a1, a2, ..., an,表示第 i 个垃圾桶处理第 i 种垃圾的时间。
接下来 n - 1 行每行两个数 x, y,表示有一条双向传送带连接编号为 x, y 的两个垃圾桶。
输出格式
输出一个整数,表示从开始投放垃圾到处理完所有垃圾的总时间。
输入输出样例1
样例输入
3 2
5 2 5
1 2
1 3
样例输出
14
样例说明
开始投放 2 秒后,1 号垃圾到达 1 号垃圾桶,2 号垃圾到达 2 号垃圾桶,3 号垃圾还没投放;
开始投放 4 秒后,2 号垃圾处理完成;
开始投放 5 秒后,3 号垃圾到达 2 号垃圾桶,此时 1 号垃圾桶仍在处理 1 号垃圾,所以 3 号垃圾无法传输, 在此等待;
开始投放 7 秒后,1 号垃圾处理完成;
开始投放 9 秒后,3 号垃圾到达 3 号垃圾桶;
开始投放 14 秒后,3 号垃圾处理完成。此时,所有垃圾都已经处理完。
输入输出样例2
样例输入
6 4
6 5 4 3 2 1
1 2
3 2
2 4
5 4
1 6
样例输出
18
样例说明
开始投放 9 秒后,1 号垃圾处理完;
开始投放 8 秒后,2 号垃圾处理完;
开始投放 14 秒后,3 号垃圾处理完;
开始投放 12 秒后,4 号垃圾处理完;
开始投放 16 秒后,5 号垃圾处理完;
开始投放 18 秒后,6 号垃圾处理完。
数据规模与约定
对于100%的数据, n <= 100000, 0 <= ai <= 10^9, 1 <= x, y <= n。
@solution@
首先转化一下问题,去除投放垃圾这一过程:
我们从 r 开始连出去 n 个点连成一条链,并把 1~n 号垃圾从前往后放置在这些点上面。即 n -> n-1 -> ... -> 1 -> r(前面的 1~n 是垃圾放置的对应结点)。
这样就只剩下垃圾在结点之间移动了。
考虑垃圾之间一般是不会互相影响的,垃圾 x 会堵塞垃圾 y,要么是因为垃圾 x 本身在处理,要么是垃圾 x 被前面某个垃圾间接堵塞了。
我们记标记 (x, t) 表示在时刻 t 之前无法通过 x,而从时刻 t 开始可以通过结点 x(通过包含到达的情况)。
注意这里的标记是一个下界,不一定时刻 t 一定就有垃圾通过结点 x。
那么标记的产生就会两种情况:某垃圾被处理了,某垃圾被间接堵塞了。
对于第 i 个垃圾,它到达的时间受它所在结点与目的地之间的路径上所有标记的影响。
它的终点为点 i,因为这些标记 (x, t) 对于 i 的影响为 t + dep[i] - dep[x]。
我们维护 max(t - dep[x]),就可以快速求出一个点到达目的地的时间。
然后考虑怎么维护标记:
先考虑间接堵塞,我们发现这个等价于所有标记上移到结点的父亲。
如果是在链上,这个操作可以用平衡树来维护(貌似也可以用线段树)。
如果是在树上,最常见的就是树链剖分转为序列问题。但是从重链顶端上移时,需要进行类区间拆分与区间合并的操作。于是就不得不用什么平衡树来做了。
貌似用 lct 要更自然一点。不过 splay 太难写了(
然后是自己被处理,会产生一个 (max(t - dep[x]) + dep[i] + a[i] +1, x) 的标记。平衡树上直接插入即可。这里的插入必须要在标记向上爬之后再插。
当然为了保证合法,我们还要加上标记 (1, 1)。
时间复杂度 O(nlog^2n)。
@accepted code@
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF = (1LL<<60);
const int MAXN = 200000;
struct node{
ull pri;
ll tg, mx, val;
int dep;
node *ch[2];
}pl[MAXN + 5], *ncnt, *NIL;
typedef pair<node*, node*> Droot;
struct treap{
treap() {
NIL = ncnt = &pl[0];
NIL->mx = -INF;
NIL->ch[0] = NIL->ch[1] = NIL;
}
ull get_rand() {
ull p = rand(), q = (p << 16) | rand();
ull u = rand(), v = (u << 16) | rand();
return (q << 32) | v;
}
node *newnode(ll x, int d) {
node *p = (++ncnt);
p->dep = d, p->mx = p->val = x;
p->pri = get_rand(), p->tg = 0;
p->ch[0] = p->ch[1] = NIL;
return p;
}
void maintain(node *x, ll k) {
if( x == NIL ) return ;
x->tg += k, x->mx += k, x->val += k, x->dep -= k;
}
void pushup(node *x) {
x->mx = max(max(x->ch[0]->mx, x->ch[1]->mx), x->val);
}
void pushdown(node *x) {
if( x->tg ) {
maintain(x->ch[0], x->tg);
maintain(x->ch[1], x->tg);
x->tg = 0;
}
}
node *merge(node *x, node *y) {
if( x == NIL ) return y;
if( y == NIL ) return x;
pushdown(x), pushdown(y);
if( x->pri < y->pri ) {
x->ch[1] = merge(x->ch[1], y);
pushup(x); return x;
}
else {
y->ch[0] = merge(x, y->ch[0]);
pushup(y); return y;
}
}
Droot split(node *x, int k) {
if( x == NIL ) return make_pair(NIL, NIL);
pushdown(x);
if( k < x->dep ) {
Droot p = split(x->ch[0], k);
x->ch[0] = p.second; pushup(x);
return make_pair(p.first, x);
}
else {
Droot p = split(x->ch[1], k);
x->ch[1] = p.first; pushup(x);
return make_pair(x, p.second);
}
}
node *insert(node *x, node *y) {
Droot p = split(x, y->dep);
return merge(p.first, merge(y, p.second));
}
}T;
struct edge{
int to; edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
int hvy[MAXN + 5], siz[MAXN + 5], dep[MAXN + 5], fa[MAXN + 5];
void dfs1(int x, int f) {
siz[x] = 1, dep[x] = dep[f] + 1, fa[x] = f, hvy[x] = 0;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
dfs1(p->to, x);
siz[x] += siz[p->to];
if( siz[hvy[x]] < siz[p->to] )
hvy[x] = p->to;
}
}
node *rt[MAXN + 5];
int top[MAXN + 5], tid[MAXN + 5], dfn[MAXN + 5], dcnt = 0;
void dfs2(int x, int tp) {
rt[x] = NIL;
top[x] = tp, tid[x] = (++dcnt), dfn[dcnt] = x;
if( !hvy[x] ) return ;
dfs2(hvy[x], tp);
for(edge *p=adj[x];p;p=p->nxt)
if( p->to != fa[x] && p->to != hvy[x] )
dfs2(p->to, p->to);
}
void insert(int x, ll k) {
node *p = T.newnode(k - dep[x], dep[x]);
rt[top[x]] = T.insert(rt[top[x]], p);
}
ll query(int x) {
ll ret = -INF;
while( x ) {
Droot p = T.split(rt[top[x]], dep[x]);
ret = max(ret, p.first->mx);
rt[top[x]] = T.merge(p.first, p.second);
x = fa[top[x]];
}
return ret;
}
void update(int x) {
node *y = NIL;
while( x ) {
Droot p = T.split(rt[top[x]], dep[x]);
T.maintain(p.first, 1);
rt[top[x]] = T.merge(p.first, p.second);
p = T.split(rt[top[x]], dep[top[x]] - 1);
rt[top[x]] = T.insert(p.second, y), y = p.first;
x = fa[top[x]];
}
}
int a[MAXN + 5], n, r;
int main() {
freopen("garbage.in", "r", stdin);
freopen("garbage.out", "w", stdout);
srand(0307); scanf("%d%d", &n, &r);
for(int i=1;i<=n;i++)
scanf("%d", &a[i]);
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
addedge(r, n + 1);
for(int i=2;i<=n;i++) addedge(n + i - 1, n + i);
dfs1(2*n, 0), dfs2(2*n, 2*n), insert(r, 1);
ll ans = 0;
for(int i=1;i<=n;i++) {
ll tmp = dep[i] + query(i) + a[i];
update(i), insert(i, tmp + 1);
ans = max(ans, tmp);
}
printf("%lld\n", ans);
}
@details@
出题人在嘲讽选手是垃圾
话说树链剖分套平衡树是否超出了 CSP 的考纲范围内。
(也不一定,毕竟去年连 ddp 都考)
@CSP模拟2019.10.16 - T3@ 垃圾分类的更多相关文章
- [NOIP2018模拟赛10.16]手残报告
[NOIP2018模拟赛10.16]手残报告 闲扯 炉石乱斗模式美滋滋啊,又颓到好晚... 上来T2先敲了树剖,看T1发现是个思博DP,然后没过大样例,写个暴力发现还是没过大样例!?才发现理解错题意了 ...
- 2019.10.16&17小结
话说也蛮久没写小结了,主要这两次考试失分严重,还是总结下吧. 10.16 T1 小奇挖矿2 100/0 [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿石交易市 ...
- A 题解————2019.10.16
[题目描述] 对于给定的一个正整数n, 判断n是否能分成若干个正整数之和 (可以重复) ,其中每个正整数都能表示成两个质数乘积. [输入描述]第一行一个正整数 q,表示询问组数.接下来 q 行,每行一 ...
- @省选模拟赛03/16 - T3@ 超级树
目录 @description@ @solution@ @accepted code@ @details@ @description@ 一棵 k-超级树(k-SuperTree) 可按如下方法得到:取 ...
- js第二次作业——2019.10.16
第一题:完成省城市的三级联动(包括湖南省),附代码和效果图. 1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 </head> ...
- C 题解———2019.10.16
现在很痛苦,等过阵子回头看看,会发现其实那都不算事. [题目描述]定义一个排列 a 的价值为满足|a[i]-i|<=1 的 i 的数量.给出三个正整数 n,m,p,求出长度为 n 且价值恰好为 ...
- B 题解————2019.10.16
相信他说的话,但不要当真 [题目描述]有一个长度为 n 的自然数序列 a,要求将这个序列恰好分成至少 m 个连续子段. 每个子段的价值为该子段的所有数的按位异或.要使所有子段的价值按位与的结果最大,输 ...
- 6377. 【NOIP2019模拟2019.10.05】幽曲[埋骨于弘川]
题目描述 题解 随便bb 详细题解见 https://www.cnblogs.com/coldchair/p/11624979.html https://blog.csdn.net/alan_cty/ ...
- 【NOIP2019模拟2019.10.07】果实摘取 (约瑟夫环、Mobius反演、类欧、Stern-Brocot Tree)
Description: 小 D 的家门口有一片果树林,果树上果实成熟了,小 D 想要摘下它们. 为了便于描述问题,我们假设小 D 的家在二维平面上的 (0, 0) 点,所有坐标范围的绝对值不超过 N ...
随机推荐
- 用CSS添加选中文字的背景色
- vue单页应用中,使用setInterval()定时向服务器获取数据,后来跳转页面后,发现还在不停的获取数据。
使用VUE开发单页项目时遇到这样的问题,mounted中使用setInterval()定时向服务器获取数据,后来跳转页面后,发现还在不停的获取数据.我以为是因为我路由用的push导致的,改成repla ...
- gitlab上fork别人的代码后更新的2种解决方式
1.解决方式1 首先要先确定一下是否建立了主repo的远程源: git remote -v如果里面只能看到你自己的两个源(fetch 和 push),那就需要添加主repo的源: git remote ...
- idea中使用本地jar包
一个maven项目中使用到了taobao-sdk-java-auto_1455552377940-20160422.jar包,项目是直接导入的jar包而没用用maven的形式导入,导致在idea编辑时 ...
- 初识zookeeper以及安装和集群部署
初识zookeeper以及安装和集群部署 一.Zookeeper单体版安装 在安装zookeeper之前要先安装jdk环境,具体在linux环境安装jdk1.8请参照linux笔记. ...
- 基于python爬虫的github-exploitdb漏洞库监控与下载
基于python爬虫的github-exploitdb漏洞库监控与下载 offensive.py(爬取项目历史更新内容) #!/usr/bin/env python # -*- coding:utf- ...
- 学习Python笔记---操作列表
1.for循环: 编写for循环时,对于用语存储列表中每个值的临时变量,可指定任何名称. 在for循环中,想包含多少行代码都可以,每个缩进的代码行都是循环的一部分,且将针对列表中的每个值都执行一次. ...
- PYTHON网络爬虫与信息提取[网络爬虫协议](单元二)
robots.txt在网站的根目录下 遵守 自动或人工识别robots.txt再进行内容爬取 约束性:建议性,不遵守协议,存在法律风险. 基本语法: User-agent: * Disallow: / ...
- oracle定制定时执行任务
1.引言 定制定时执行的任务有两种形式,系统级别和数据库级别, 从操作系统级别来讲, windows系统我们可以使用任务计划来实现, 对于winXP系统,设置步骤如下,开始---设置---控制面板-- ...
- 【并查集模板】 【洛谷P2978】 【USACO10JAN】下午茶时间
P2978 [USACO10JAN]下午茶时间Tea Time 题目描述 N (1 <= N <= 1000) cows, conveniently numbered 1..N all a ...