zoukankan      html  css  js  c++  java
  • JZOJ 5415. 【NOIP2017提高A组集训10.22】公交运输

    题目

    城市中有一条长度为 (n) 的道路,每隔 (1) 的长度有一个公交车站,编号从 (0)(n),学校在 (0) 号车站的位置。其中每个公交车站(除了 (n) 号车站)有两个属性 (c_i)(v_i),代表从这个公交车站出发的公交车的性质。(c_i) 代表这个从i出发的公交车,相邻两个停靠站之间的距离。(v_i) 表示每坐 (1) 站的花费。
    注意,一辆公交车出发后会向 (n) 号车站的方向行进。同时,一名乘客只能从起点站上车,但可以从任意停靠站下车。校庆志愿者小 (Z) 为了帮助校友查询有关城市交通费用的问题,想知道从 (0) 号车站(也就是学校)出发,到达每个公交车站的最小花费,于是他找到了你。

    数据规模

    对于 30% 的数据满足,(1 le n le 5000)
    对于 60% 的数据满足,(1 le n le 10^5)
    对于另 20% 的数据满足,(maxc = 1)
    对于 100% 的数据满足,(1 le n le 10^6,1 le maxc le 10,1 le ci le maxc,1 le vi le 1000)
    数据存在梯度。

    分析

    30% 的数据

    我们试着想 (O(n^2))(dp)
    (f_i = min_{1 le j < i,c_j | (i-j)}f_j + (i-j)/c_j*v_j)

    另 20% 的数据

    既然有 (maxc = 1) 说明所有的 (c_j=1)
    也就是说我们从 (0) 点开始坐车,一遇到 (v_i) 更小的就可以换乘,必然更优
    (O(n)) 扫一遍就好了

    100% 的数据

    考虑继续优化 (dp)
    很明显只能上斜率优化了!
    很明显用不了斜率优化!

    观察 (f_i = f_j+ (i-j)/c_j*v_j)
    若需 (c_j|(i-j)),那么 (i equiv j pmod {c_j})
    所以我们要维护 (i) 的决策集合
    只要 (c_i)(i mod {c_i}) 两样东西就可以确定了
    那我们就维护 (maxc imes maxc) 个决策集合
    选用 (vector) 类型就好,我选择了链式前向星
    然后怎么找最优决策?

    我们先从题目中找性质
    两站同属一个决策集合
    如过后一个站的 (v_i) 小于 前一个站 (v_i), 前一个站就没有用了
    然后我们就有了 (v_i) 单调递增
    因为同属一个集合,(c_i=c_j) 所以 (v_i/c_i) 单调递增
    (p_i = v_i / c_i)
    那么 (f_i = f_j+(i-j)*p_i)
    此时考虑斜率优化
    (j < k)(j) 更优

    [egin{aligned} f_j+(i-j)*p_j < f_k+(i-k)*p_k \ frac{(f_j-j*p_j)-(f_k-k*p_k)}{p_k-p_j} < i end{aligned} ]

    因为 (f_j-j*p_j) 越来越小
    所以最优决策在最后,用栈维护集合即可

    (Code)

    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    const int N = 1e6 + 5, INF = 0x3f3f3f3f;
    int n, maxc;
    int f[N], c[N], v[N], t[15][15];
    
    struct node{int to, pre;}e[N];
    inline void add(int c, int y, int to)
    {
    	static int tot = 0;
    	e[++tot] = node{to, t[c][y]}, t[c][y] = tot;
    }
    
    inline double slope(int j, int k){return (f[j] - 1.0 * j * v[j] / c[j] - f[k] + 1.0 * k * v[k] / c[k]) / (v[k] - v[j]);}
    
    inline void read(int &x)
    {
    	x = 0; int f = 1; char ch = getchar();
    	while (ch < '0' || ch > '9') f = (ch == '-' ? -1 : f), ch = getchar();
    	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    	x *= f;
    }
    int buf[20];
    inline void write(int x)
    {
    	if (x < 0) putchar('-'), x = -x;
    	for (; x; x /= 10) buf[++buf[0]] = x % 10;
    	if (!buf[0]) buf[++buf[0]] = 0;
    	for (; buf[0]; putchar('0' + buf[buf[0]--]));
    }
    
    int main()
    {
    	freopen("bus.in", "r", stdin);
    	freopen("bus.out", "w", stdout);
    	read(n), read(maxc);
    	for(register int i = 0; i < n; i++) read(c[i]), read(v[i]);
    	f[0] = 0, add(c[0], 0, 0);
    	for(register int i = 1; i <= n; i++)
    	{
    		f[i] = INF;
    		for(register int j = 1; j <= maxc; j++)
    		if (t[j][i % j])
    		{
    			int y = i % j;
    			while (e[t[j][y]].pre && slope(e[e[t[j][y]].pre].to, e[t[j][y]].to) < 1.0 * i / j) t[j][y] = e[t[j][y]].pre;
    			f[i] = min(f[i], f[e[t[j][y]].to] + (i - e[t[j][y]].to) * v[e[t[j][y]].to] / c[e[t[j][y]].to]);
    		}
    		if (i == n || f[i] == INF) continue;
    		int y = i % c[i];
    		while (t[c[i]][y] && v[e[t[c[i]][y]].to] >= v[i]) t[c[i]][y] = e[t[c[i]][y]].pre;
    		while (e[t[c[i]][y]].pre && slope(e[e[t[c[i]][y]].pre].to, e[t[c[i]][y]].to) < slope(e[t[c[i]][y]].to, i)) 
    			t[c[i]][y] = e[t[c[i]][y]].pre;
    		add(c[i], y, i);
    	}
    	for(register int i = 1; i <= n; i++) 
    	if (f[i] == INF) putchar('-'), putchar('1'), putchar(' ');
    	else write(f[i]), putchar(' ');
    }
    
  • 相关阅读:
    PHP中读写文件
    HTMLmeta
    穷举密码序列(C#)
    多线程访问winform控件示例
    Web服务器
    [转]javascript 之 select下拉框
    大中小字体显示
    WPF中StringFormat的用法
    常用的 19 条 MySQL 优化
    resin部署安装
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/14296169.html
Copyright © 2011-2022 走看看