首页 > 其他 > 详细

JZOJ5415. 公交运输

时间:2021-01-18 09:25:46      阅读:29      评论:0      收藏:0      [点我收藏+]

题目大意

在数轴上从\(0\)\(n\)\(n+1\)个点, 其中\(0\text{到}n-1\)\(n\)个位置分别有公交车, 只能向数轴正方向移动, 每个点的公交车有两个属性\(c_i\)\(v_i\), 分别表示隔几个点停一次(一站要经过几个点)坐一站的花费, 求从\(0\)到每个点的最小花费, 无法到达者输出\(-1\).
\(n<=10^6, maxc<=10.\)

解题思路

简单思考得到一个dp:设\(f_i\)\(i\)点的最小花费, 转移显然.
思考如何优化转移:设当前已经求完答案的点为\(i\), 那么这个点对后面的点的影响可以看做一个一次函数(直线), 斜率为\(\frac{v_i}{c_i}\), 且有一个点\((i, f[i])\), 凭此即可确定函数解析式.
然而有一个问题:不是每一个点都能用这条直线来更新答案的.
怎样的点能被这条直线更新呢? 也就是说这条直线只有在公交车停的站才有贡献嘛! 所以满足条件的点的集合是

\[\{j|j>i\text{且}c_i|(j-i)\} \]

\[\{j|j>i\text{且}j\equiv i(\operatorname{mod}\ c_i)\} \]

也就是只要\(j\operatorname{mod}c_i = i\operatorname{mod}c_i\) 即可被转移.
看数据范围:\(maxc<=10\)! 由于一共最多只有\(10\)\(c\), 且\(i\operatorname{mod}c_i\)的取值最多不超过\(c_i\)种, 于是总共最多只有\(\sum_1^{10}i\)种情况, 即\(55\)种情况.

所以... 按照\(55\)种情况分类的话...
我们只用开\(55\)棵李超树就好了!!!

开这么多棵树空间复杂度怎么办呢? 容易想到动态开点. 由于一共只有\(n\)条直线, 所以只会有\(n\)个树节点.
对于每一个\(j\), 在李超树上枚举每一种\(c\), 设枚举的值为\(p\), 查询\((p, j \operatorname{mod} p)\)对应的李超树. 求完答案后丢进李超树即可.
由于李超树查询的复杂度是\(O(\log{n})\), 插入的复杂度是\(O(\log^2{n})\), 一个点要做\(maxc\)次查询和\(1\)次插入, 所以总时间复杂度为\(O(n(maxc\cdot\log{n}+\log^2{n}))\)
虽然\(n<=10^6\), 但是由于 数据可能是随机的 做人要有信仰 节点数比较少根本跑不满,并且时限2s, 所以简 单 的常数优化以后就能过了.

后记

这种题, 思路很自然, 但是时间复杂度比较清奇, 所以考场没敢写. 实际上就算被卡了也会有\(80pts\), 所以以后要大胆写算法, 大胆开数据结构, 有时间会学习优秀的单调栈做法咕咕咕咕...

#include <cstdio>
#include <cstring>
#include <iostream>
#define N 1000010
#define db double
#define ll long long
#pragma GCC optimize(2)
#define INF 0x3f3f3f3f3f3f3f3fll
#define init(a, b) memset(a, b, sizeof(a))
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
inline int read()
{
	int x = 0; char ch = getchar();
	while(ch < ‘0‘ || ch > ‘9‘)	ch = getchar();
	while(ch >= ‘0‘ && ch <= ‘9‘)	x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return x;
}
int n, c[N], v[N];
ll f[N];
struct Line
{
	db k, b;
	Line(){k = 0, b = INF;}
	Line(db _k, db x, db y){k = _k; b = y - k * x;}
	inline db y(db x){return k * x + b;}
};
namespace Seg
{
	int cnt, to[11][11], rt[60], ls[N], rs[N];
	Line tr[N];
	inline void build(int k, int i){static int tot = 0; !rt[to[k][i]] && (rt[to[k][i] = ++tot] = ++cnt);}
	void insert(int t, int l, int r, Line p)
	{
		int mid = (l + r) >> 1;
		db lx = tr[t].y(l) - p.y(l), mx = tr[t].y(mid) - p.y(mid), rx = tr[t].y(r) - p.y(r);
		if(lx >= 0 && rx >= 0)	tr[t] = p;
		else if(lx < 0 && rx >= 0)
		{
			if(mx >= 0)	!ls[t] && (ls[t] = ++cnt), insert(ls[t], l, mid, tr[t]), tr[t] = p;
			else !rs[t] && (rs[t] = ++cnt), insert(rs[t], mid + 1, r, p);
		}
		else if(lx >= 0 && rx < 0)
		{
			if(mx >= 0)	!rs[t] && (rs[t] = ++cnt), insert(rs[t], mid + 1, r, tr[t]), tr[t] = p;
			else !ls[t] && (ls[t] = ++cnt), insert(ls[t], l, mid, p);
		}
	}
	inline void insert(int k, int i, Line p){build(k, i); insert(rt[to[k][i]], 1, n, p);}
	db query(int t, int l, int r, int x)
	{
		db ret = tr[t].y(x);
		int mid = (l + r) >> 1;
		if(x <= mid){if(ls[t])	ret = min(ret, query(ls[t], l, mid, x));}
		else if(rs[t])	ret = min(ret, query(rs[t], mid + 1, r, x));
		return ret;	
	}
	inline db query(int k, int i, int x){return rt[to[k][i]] ? query(rt[to[k][i]], 1, n, x) : INF;}
}
int main()
{
	freopen("bus.in", "r", stdin);
	freopen("bus.out", "w", stdout);
	n = read(), c[0] = read();
	fo(i, 1, n)	c[i - 1] = read(), v[i - 1] = read();
	Seg::insert(c[0], 0, Line(1.0 * v[0] / c[0], 0.0, 0.0));
	fo(i, 1, n)
	{
		f[i] = INF;
		fo(j, 1, 10)
			f[i] = min(f[i], (ll)(Seg::query(j, i % j, i) + 0.5));
		printf("%lld ", f[i] == INF ? -1 : f[i]);
		f[i] != INF && i < n && (Seg::insert(c[i], i % c[i], Line(1.0 * v[i] / c[i], i, f[i])), 1);
	}
	return 0;
}

JZOJ5415. 公交运输

原文:https://www.cnblogs.com/Martin-MHT/p/14291079.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!