zoukankan      html  css  js  c++  java
  • @CSP模拟2019.10.16


    @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
    ", ans);
    }
    

    @details@

    出题人在嘲讽选手是垃圾

    话说树链剖分套平衡树是否超出了 CSP 的考纲范围内。
    (也不一定,毕竟去年连 ddp 都考)

  • 相关阅读:
    git commit之后,想撤销commit
    centOS7下ifconfig提示command not found
    图数据库 — neo4j (二)
    XQuery的 value() 方法、 exist() 方法 和 nodes() 方法
    SQL存储过程-新增和修改,参数Xml数据类型
    项目管理软件之易度1.5,禅道2.0,redmine1.2(附redmine1.2的安装)
    测试性能工具
    存储过程
    人生三支点:健康,职业,自由
    生活网站推荐
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11697789.html
Copyright © 2011-2022 走看看