zoukankan      html  css  js  c++  java
  • XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki Problem J Stairways解题报告(分块+维护凸壳)

    首先ORZ一发Claris聚聚的题解:http://www.cnblogs.com/clrs97/p/8689215.html,不然我可能没机会补过这道神题了。

    这里写一个更详细的题解吧(我还是太菜了啊)。

    题目描述

    有(n(n le10^5))个人依次进入一个入口,要到一个出口。入口到出口有两条同样长的路。每个人都有一个速度,用通行时间(a_i(1le a_i le 10^6))表示,他可以选择任一条路走。但是,若走这条路的前面的人比他慢的话,他只能降到和前面所有人最慢的那个人同样的速度(从而会多花时间)。现在请规划每个人选哪条路,使得每个人因等前面的人而浪费的时间尽可能少。

    Sample Input

    5
    100 4 3 2 1

    Sample Output

    6

    详细题解

    此题很容易用DP来做。考虑前(i)个人,则两个楼梯必有一个的通行时间变为前(i)个人最慢的那个,我们设(dp[i][j])表示前(i)个人另一个楼梯当前通行时间是(j)((j)从小到大离散化)时的最优答案,则考虑(dp[i+1])和(dp[i])的关系:

    (1)若(a[i+1] ge max(a[1..i])),则显然(dp[i+1][j]=dp[i][j]);

    (2)若(a[i+1]<max(a[1..i])),则:

    情况1:(j)对应状态快的那个楼梯比(a[i+1])时间短,且选这个楼梯,于是(dp[i+1][k]=min(dp[i][j],jle k)),其中(k)为(a[i+1])离散化的结果;

    情况2:(j)对应状态快的那个楼梯比(a[i+1])时间短,但选最慢的楼梯,于是(dp[i+1][j]=dp[i][j]+max(a[1..i])-a[i+1]),其中(j<k);

    情况3:(j)对应状态快的那个楼梯比(a[i+1])的时间长,那必然选这个楼梯,于是(dp[i+1][j]=dp[i][j]+f[j]-a[i+1]),其中(j>k),(f[j])表示第(j)小的值。

    这样状态数和转移复杂度均为(n^2)。下面考虑数据结构优化。

    我们需要维护的dp要支持区间最小值查询,单点修改,区间增加,和区间(dp[i][j]+=f[j])。

    如果没有最后的操作此题直接用线段树就简单多了。

    加上了这种操作,考虑分块。每块首先要维护增量tag,该tag对最值无影响。下面主要考虑(dp[i][j]+=f[j])。

    注意到一个性质:若((dp[i][j+1]-dp[i][j])/(f[j+1]-f[j])<(dp[i][j]-dp[i][j-1])/(f[j]-f[j-1])),那么无论再怎么增加(dp[i][j])也不可能最优。所以将(j)下标看做二维点((f[j],dp[i][j]))后,所有可能的最优值形成一个下凸壳。当整块(dp[i][j]+=f[j])后,凸壳上仍是这些点,但最小值点可能将向左移动。于是我们只要不断删除凸壳右边的点,就可以每一块均摊(O(1))的修改和查询最小值。

    对于单点修改,只需要重构凸壳,复杂度为块大小。

    现在考虑分块后从(dp[i])转移到(dp[i+1])的总复杂度,设块大小(b)。由于单点修改仅一个点(k),故复杂度(b);取最小值复杂度(b+n/b);区间加复杂度(b+n/b);区间(dp[i][j]+=f[j])复杂度(b+n/b)。当(b)取(sqrt n) 时复杂度最优,为(sqrt n )。考虑到重构凸壳较慢,应在求最值时如需要再重构凸壳。

    总时间复杂度(O(n sqrt n))

    AC代码

      1 #include<cstdio>
      2 #include<cmath>
      3 #include<algorithm>
      4 #include<cstring>
      5 using namespace std;
      6 #define LL long long
      7 struct Block{
      8     LL a[400], tag, delta;
      9     int order[400];
     10     int pos[400], back, n;
     11     bool flag;
     12     void init(int b[], int size){
     13         n = size; flag = true;
     14         memcpy(order, b, sizeof(int)*n);
     15         memset(a, 0x3f, sizeof(LL)*n);
     16     }
     17     bool check(int j1, int j, int j2){
     18         return (a[j2] - a[j]) * (order[j] - order[j1]) <= (a[j] - a[j1]) * (order[j2] - order[j]);
     19     }
     20     LL get(int i){ return a[i] + tag * order[i] + delta; }
     21     void update(){
     22         for (int i = 0; i < n; i++)
     23             a[i] = get(i);
     24         tag = delta = 0; back = 0;
     25         flag = false;
     26         for (int i = 0; i < n; i++){
     27             while (back>1 && check(pos[back - 1], pos[back], i))back--;
     28             pos[++back] = i;
     29         }
     30         while (back > 1 && get(pos[back - 1]) <= get(pos[back]))back--;
     31     }
     32     void set(int i, LL val){
     33         a[i] += val - get(i);
     34         flag = true;
     35     }
     36     void add(int l, int r, int d){
     37         if (l == 0 && r == n - 1)delta += d;
     38         else{
     39             for (int i = l; i <= r; i++)
     40                 a[i] += d;
     41             flag = true;
     42         }
     43     }
     44     void add2(int l, int r){
     45         if (l == 0 && r == n - 1){
     46             tag++;
     47             while (back > 1 && get(pos[back - 1]) <= get(pos[back]))back--;
     48         }
     49         else{
     50             for (int i = l; i <= r; i++)
     51                 a[i] += order[i];
     52             flag = true;
     53         }
     54     }
     55     LL queryMin(int l, int r){
     56         if (l == 0 && r == n - 1){
     57             if (flag)update();
     58             return get(pos[back]);
     59         }
     60         LL ret = 1LL << 60;
     61         for (int i = l; i <= r; i++)
     62             ret = min(ret, get(i));
     63         return ret;
     64     }
     65 }b[1000];
     66 int a[100002], order[100002];
     67 int belong[100002], offset[100002], blockSize;
     68 void add(int l, int r, int delta){
     69     int start = l / blockSize, end = r / blockSize;
     70     for (int i = start; i <= end; i++)
     71         b[i].add(i == start ? offset[l] : 0, i == end ? offset[r] : b[i].n - 1, delta);
     72 }
     73 void add2(int l, int r){
     74     int start = l / blockSize, end = r / blockSize;
     75     for (int i = start; i <= end; i++)
     76         b[i].add2(i == start ? offset[l] : 0, i == end ? offset[r] : b[i].n - 1);
     77 }
     78 LL queryMin(int l, int r){
     79     int start = l / blockSize, end = r / blockSize;
     80     LL ret = 1LL << 60;
     81     for (int i = start; i <= end; i++)
     82         ret = min(ret, b[i].queryMin(i == start ? offset[l] : 0, i == end ? offset[r] : b[i].n - 1));
     83     return ret;
     84 }
     85 int main(){
     86     int n;
     87     scanf("%d", &n);
     88     for (int i = 1; i <= n; i++){
     89         scanf("%d", &a[i]);
     90         order[i] = a[i];
     91     }
     92     order[0] = 0;
     93     sort(order, order + n + 1);
     94     int cnt = unique(order, order + n + 1) - order;
     95     blockSize = sqrt(cnt);
     96     int j = 0, k = 0;
     97     for (int i = 0; i < cnt; i++){
     98         belong[i] = k;
     99         offset[i] = j++;
    100         if (j == blockSize){
    101             b[k].init(order + i - j + 1, j);
    102             j = 0; k++;
    103         }
    104     }
    105     if (j)b[k].init(order + cnt - j, j);
    106     b[0].set(0, 0);
    107     int mpos = 0;
    108     for (int i = 1; i <= n; i++){
    109         int pos = lower_bound(order, order + cnt, a[i]) - order;
    110         if (pos >= mpos)mpos = pos;
    111         else{
    112             LL val = queryMin(0, pos);
    113             b[belong[pos]].set(offset[pos], val);
    114             add(0, pos - 1, order[mpos] - order[pos]);
    115             add(pos + 1, mpos, -order[pos]);
    116             add2(pos + 1, mpos);
    117         }
    118     }
    119     printf("%lld", queryMin(0, cnt - 1));
    120 }
  • 相关阅读:
    前端代码美化的艺术
    CSS3 简单的砸金蛋样式
    为什么 VS Code 能迅速占领 JavaScript 开发者社区
    了解并使用 CSS 中的 rem 单位
    CSS中一些利用伪类、伪元素和相邻元素选择器的技巧
    SVG入门指南
    PAT 1035 Password [字符串][简单]
    生信学习-二代测序知乎专栏总结[转]
    PAT 1119 Pre- and Post-order Traversals [二叉树遍历][难]
    生信笔记-mooc【武大】
  • 原文地址:https://www.cnblogs.com/zbh2047/p/9631762.html
Copyright © 2011-2022 走看看