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,否则我可能没机会补过这道神题了。php

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

题目描述

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

Sample Input优化

5
100 4 3 2 1

Sample Outputspa

6

详细题解

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

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

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

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

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

状况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 }
相关文章
相关标签/搜索