XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki Problem J Stairways解题报告(分块+维护凸壳
Posted zbh2047
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
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]>=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],j<=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 }
以上是关于XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki Problem J Stairways解题报告(分块+维护凸壳的主要内容,如果未能解决你的问题,请参考以下文章
XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki Problem J Stairways解题报告(分块+维护凸壳
XVI Open Cup named after E.V. Pankratiev. GP of Ekaterinburg.
XVII Open Cup named after E.V. Pankratiev. XXI Ural Championship
XV Open Cup named after E.V. Pankratiev. GP of Siberia-Swimming
XVII Open Cup named after E.V. Pankratiev. Eastern Grand Prix. Problem F. Buddy Numbers 贪心数论构造
XVII Open Cup named after E.V. Pankratiev Grand Prix of Moscow Workshops, Sunday, April 23, 2017 Pro