• 【BZOJ 3672】【UOJ #7】【NOI 2014】购票


    http://www.lydsy.com/JudgeOnline/problem.php?id=3672

    http://uoj.ac/problem/7

    链上的情况可以用斜率优化dp。树上用斜率优化dp时,单调队列的复杂度是均摊$O(n)$的,所以放到树上做“可持久化单调队列”复杂度是$O(n^2)$的,所以不能树上斜率优化。

    这道题可以用树链剖分(时间复杂度$O(nlog^3n)$)或者点分治套cdq分治(时间复杂度$O(nlog^2n)$)。因为树链剖分感觉比较难写,而且每个节点用vector存单调队列,显得比较卡空间,而且时间复杂度多一个log,所以写了点分治。

    对于一个点$i$,从i到根的路径上有$j$,$k$。假设$k$的深度比$j$小,且用$k$来更新$i$比$j$更优,得出式子($dis$为到根的距离):

    $$frac{f_j-f_k}{dis_j-dis_k}>p_i$$

    对于每个点,把$dis$看成横坐标,$f$看成纵坐标,最优点一定在下凸壳上。

    对于一棵树,求出分治重心,再对重心上方的子树进行分治,分治完后重心到该树的根上所有的点的$f$值都求好了,然后就用重心到根这条链上所有的点去更新重心子树中所有的点。

    先对重心子树中所有的点(不包括重心)按“$l$值-到重心的距离”排序,保证用来更新“子树中的点”的“重心到根上的点”到重心的距离单调递增,这样拿单调栈来维护一个下凸壳来更新就可以了。

    时间复杂度$O(nlog^2n)$。

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N = 200003;
    int in() {
    	int k = 0, fh = 1; char c = getchar();
    	for(; c < '0' || c > '9'; c = getchar())
    		if (c == '-') fh = -1;
    	for(; c >= '0' && c <= '9'; c = getchar())
    		k = (k << 3) + (k << 1) + c - '0';
    	return k * fh;
    }
    ll inll() {
    	ll k = 0; int fh = 1; char c = getchar();
    	for(; c < '0' || c > '9'; c = getchar())
    		if (c == '-') fh = -1;
    	for(; c >= '0' && c <= '9'; c = getchar())
    		k = (k << 3) + (k << 1) + c - '0';
    	return k * fh;
    }
    
    bool vis[N];
    struct node {
    	int nxt, to; ll w;
    	node(int _nxt = 0, int _to = 0, ll _w = 0) : nxt(_nxt), to(_to), w(_w) {}
    } E[N << 1];
    int t, n, fa[N], Q[N], cnt = 0, sz[N], point[N];
    ll f[N], fadis[N], p[N], q[N], l[N], longdis[N];
    
    void ins(int u, int v, ll w) {E[++cnt] = node(point[u], v, w); point[u] = cnt;}
    
    int findrt(int x) {
    	int u, head = 0, tail = 1; Q[1] = x;
    	while (head != tail) {
    		u = Q[++head]; sz[u] = 1;
    		for(int i = point[u]; i; i = E[i].nxt)
    			if (!vis[E[i].to] && E[i].to != fa[u])
    				Q[++tail] = E[i].to;
    	}
    	for(int i = tail; i >= 1; --i) {
    		if ((sz[Q[i]] << 1) > tail) return Q[i];
    		sz[fa[Q[i]]] += sz[Q[i]];
    	}
    }
    
    int tot, qu[N];
    struct data {
    	ll dis, line; int id;
    	data(ll _dis = 0, ll _line = 0, int _id = 0) : dis(_dis), line(_line), id(_id) {}
    	bool operator < (const data &A) const {
    		return line < A.line;
    	}
    } a[N];
    
    double k_num(int x, int y) {
    	return 1.0 * (f[x] - f[y]) / (longdis[x] - longdis[y]);
    }
    
    int find(int le, double k) {
    	int left = 0, right = le - 1, mid;
    	while (left < right) {
    		mid = (left + right) >> 1;
    		if (k_num(qu[mid], qu[mid + 1]) > k) left = mid + 1;
    		else right = mid;
    	}
    	if (left == le - 1 && k_num(qu[left], qu[le]) > k) return qu[le];
    	return qu[left];
    }
    
    int cont = 0;
    void cdq(int x) {
    	vis[x] = true;
    	int i, tmp, tail, head = 0, up = x; ll len = 0;
    	for(tmp = point[x]; tmp; tmp = E[tmp].nxt)
    		if (!vis[E[tmp].to] && E[tmp].to == fa[x]) {
    			while (!vis[fa[up]] && up != 1) up = fa[up];
    			cdq(findrt(up));
    			break;
    		}
    	
    	tot = 0;
    	for(i = point[x]; i; i = E[i].nxt)
    		if (!vis[E[i].to] && E[i].to != fa[x])
    			a[++tot] = data(fadis[E[i].to], l[E[i].to] - fadis[E[i].to], E[i].to);
    	
    	while (head != tot) {
    		++head;
    		for(i = point[a[head].id]; i; i = E[i].nxt)
    			if (!vis[E[i].to] && E[i].to != fa[a[head].id])
    				a[++tot] = data(fadis[E[i].to] + a[head].dis, l[E[i].to] - fadis[E[i].to] - a[head].dis, E[i].to);
    	}
    	
    	stable_sort(a + 1, a + tot + 1);
    	
    	tmp = x;
    	while (tmp != up) {
    		tmp = fa[tmp]; if (longdis[x] - longdis[tmp] > l[x]) break;
    		if (f[x] == -1) f[x] = f[tmp] + (longdis[x] - longdis[tmp]) * p[x] + q[x];
    		else f[x] = min(f[x], f[tmp] + (longdis[x] - longdis[tmp]) * p[x] + q[x]);
    	}
    	
    	tail = 0; tmp = x; qu[0] = x;
    	for(i = 1; i <= tot; ++i) {
    		if (a[i].line < 0) continue;
    		while (tmp != up && len + fadis[tmp] <= a[i].line) {
    			len += fadis[tmp];
    			tmp = fa[tmp];
    			while (tail && k_num(tmp, qu[tail]) > k_num(qu[tail], qu[tail - 1]))
    				--tail;
    			qu[++tail] = tmp;
    		}
    		head = find(tail, (double) p[a[i].id]);
    		if (f[a[i].id] == -1) f[a[i].id] = f[head] + (longdis[a[i].id] - longdis[head]) * p[a[i].id] + q[a[i].id];
    		else f[a[i].id] = min(f[a[i].id], f[head] + (longdis[a[i].id] - longdis[head]) * p[a[i].id] + q[a[i].id]);
    	}
    	
    	for(i = point[x]; i; i = E[i].nxt)
    		if (!vis[E[i].to] && E[i].to != fa[x])
    			cdq(findrt(E[i].to));
    }
    
    int main() {
    	n = in(); t = in();
    	for(int i = 2; i <= n; ++i) {
    		fa[i] = in(); fadis[i] = inll();
    		ins(fa[i], i, fadis[i]);
    		ins(i, fa[i], fadis[i]);
    		p[i] = inll(); q[i] = inll(); l[i] = inll();
    	}
    	
    	memset(f, -1, sizeof(ll) * (n + 1));
    	f[1] = 0;
    	int u, head = 0, tail = 1;
    	Q[1] = 1; longdis[1] = 0;
    	while (head != tail) {
    		u = Q[++head];
    		for(int i = point[u]; i; i = E[i].nxt)
    			if (E[i].to != fa[u]) {
    				longdis[E[i].to] = longdis[u] + fadis[E[i].to];
    				Q[++tail] = E[i].to;
    			}
    	}
    	
    	cdq(findrt(1));
    	for(int i = 2; i <= n; ++i)
    		printf("%lld
    ", f[i]);
    	return 0;
    }
    

    终于调完了,写完后有好多错QAQ

  • 相关阅读:
    0108 创建表约束
    Mybatis 将数据库中查出的记录,一对多返回,即分组,然后返回每个组的所有数据
    SQL主表、从表
    MySQL中添加、删除字段,使用SQL语句操作
    git 将远程工作分支合并到本地dev分支
    MySQL inner join 和 left join 的区别
    Mysql union 和 order by 同时使用需要注意的问题
    The used SELECT statements have a different number of columns
    Every derived table must have its own alias(MySQL报错:每个派生表都必须有自己的别名)
    MySQL 日期格式化及字符串、date、毫秒互相转化
  • 原文地址:https://www.cnblogs.com/abclzr/p/5860430.html
Copyright © 2020-2023  润新知