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(1\le 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 }

原文地址:https://www.cnblogs.com/zbh2047/p/9631762.html

时间: 2024-10-13 00:16:54

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

A. Ability Draft 记忆化搜索. #include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #i

XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Peterhof.

A. City Wall 一圈一圈绕. B. Domino Colorings C. Сounterquestion 枚举 permutation 后 \(O(len)\) 地 check. D. Galaxy Center 同一层至多经过一个点. 可能 vis 的点不会太多,枚举在哪个点相遇. E. IBM 1403 预处理序列自动机,即可求 \(O(|s|)\) 出当前时间 t 下,打印字符串 s 的时间. F. Line Tracing G. The Queen and the Knigh

XVII Open Cup named after E.V. Pankratiev. Grand Prix of America (NAIPC-2017)

A. Pieces of Parentheses 将括号串排序,先处理会使左括号数增加的串,这里面先处理减少的值少的串:再处理会使左括号数减少的串,这里面先处理差值较大的串.确定顺序之后就可以DP了. 时间复杂度$O(n^3)$. #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=310,inf=1000000; int n,i,j,m,all,

XVII Open Cup named after E.V. Pankratiev Grand Prix of Moscow Workshops, Sunday, April 23, 2017 Problem D. Great Again

题目: Problem D. Great AgainInput file: standard inputOutput file: standard outputTime limit: 2 secondsMemory limit: 512 megabytesThe election in Berland is coming. The party United Berland is going to use its influence to win themagain. The crucial co

XVII Open Cup named after E.V. Pankratiev Grand Prix of Moscow Workshops, Sunday, April 23, 2017 Problem K. Piecemaking

题目:Problem K. PiecemakingInput file: standard inputOutput file: standard outputTime limit: 1 secondMemory limit: 512 mebibytesThe civil war in Berland continues for five years already. The United Nation decided to end the bloodshed.Berland consists o

XVIII Open Cup named after E.V. Pankratiev. GP of Romania

A. Balance 不难发现确定第一行第一列后即可确定全部,列不等式单纯形求解线性规划即可. #include<cstdio> #include<algorithm> using namespace std; typedef long long ll; typedef vector<double>VD; const int N=110; const double eps=1e-9; VD simplex(vector<VD>A, VD b, VD c){

XVIII Open Cup named after E.V. Pankratiev. GP of SPb

contest Link A. Base i ? 1 Notation solved by sdcgvhgj 238 B. Squaring a Bit solved by rdc 77 本地打表,交表. C. Chickens solved by rdc, 24 f[i][mask] 表示前 i 个物品匹配 mask 集合的方案数. D. Lights at a Crossing E. Decimal Form solved by rdc, 247 -2 类欧或者SB树,然后发现自己写的类欧板

XVII Open Cup named after E.V. Pankratiev. XXI Ural Championship

A. Apple 按题意模拟即可. #include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include

XVI Open Cup named after E.V. Pankratiev. GP of Ekaterinburg.

贼惨 130/186 B Black Widow 简单题 #include <bits/stdc++.h> const long long mod = 1e9+7; const double ex = 1e-10; #define inf 0x3f3f3f3f using namespace std; map <int,int> M; int a[1010]; int b[100010]; int main() { int N; scanf("%d",&