http://codeforces.com/contest/602/problem/D
这题需要注意到的是,对于三个点(x1, y1)和(x2, y2)和(x3, y3)。如果要算出区间[1, 3]的L(h)函数的最大值,则一定不会是
通过(y3 - y1) / (x3 - x1)算出。因为很简单,通过(x2, y2)作为辅助点,数值将会更加大。
然后设dis[i]表示abs(a[i + 1] - a[i])。那么区间[L, R]的最大值就是在dis[]中,[L, R - 1]数值的最大值。
因为,不断套用上面那个结论,先从两个点的时候出发,a[2] - a[1]就是最大,然后,如果三个点,那么可能是a[3] - a[2]和a[2] - a[1]中的较大者。4个点的时候同理,每次只需要用前一个的最大值和a[new] - a[new - 1]比较,取最大的即可。
比如样例
a[]: 1、5、2、9、1、3、4、2、1、7
dis[]: 4、3、7、8、2、1、2、1、6
但是还是要枚举每个子区间,那么复杂度还是不变。
那么要把问题转化下,转化成求以dis[i]为最大值的区间数有多少个。
那么可以用单调栈维护出tonext[i]表示右边第一个大于dis[i]的数。topre[i]表示左边第一个大于dis[i]的数。
注意判断不要超过[L, R]的范围。
然后两个数值相乘,就是dis[i]为最大值的区间数。
但是有点bug。比如上面的 2、1、2、1
算了第一个2,那么后面的2就不应该重复计算。那么需要标记是否vis[]
如果vis[],那么需要找到第一个等于2的数就行了,所以我用了三次单调栈。
感觉有点复杂。
但是真正自己想到了的话,写代码是很愉快的。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <assert.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> const int maxn = 100000 + 20; int a[maxn]; int dis[maxn]; int tonext[maxn]; int topre[maxn]; int topresec[maxn]; int vis[maxn]; int tdis[maxn]; struct node { int id; int val; }STACK[maxn]; void work() { int n, q; // IOS; // cin >> n >> q; scanf("%d%d", &n, &q); for (int i = 1; i <= n; ++i) { // cin >> a[i]; scanf("%d", &a[i]); } for (int i = 1; i <= n - 1; ++i) { dis[i] = abs(a[i + 1] - a[i]); tdis[i] = dis[i]; } sort(tdis + 1, tdis + 1 + n - 1); dis[n] = inf; int top = 1; STACK[top].id = 1; STACK[top].val = dis[1]; for (int i = 2; i <= n; ++i) { while (top >= 1 && dis[i] > STACK[top].val) { tonext[STACK[top].id] = i; top--; } ++top; STACK[top].id = i; STACK[top].val = dis[i]; } // for (int i = 1; i <= n; ++i) { // printf("%d ", tonext[i]); // } dis[0] = inf; top = 1; STACK[top].id = n - 1; STACK[top].val = dis[n - 1]; for (int i = n - 2; i >= 0; --i) { while (top >= 1 && dis[i] > STACK[top].val) { topre[STACK[top].id] = i; --top; } ++top; STACK[top].id = i; STACK[top].val = dis[i]; } dis[0] = inf; top = 1; STACK[top].val = dis[n - 1]; STACK[top].id = n - 1; for (int i = n - 2; i >= 0; --i) { while (top >= 1 && dis[i] >= STACK[top].val) { topresec[STACK[top].id] = i; --top; } ++top; STACK[top].id = i; STACK[top].val = dis[i]; } // for (int i = 1; i <= n; ++i) { // printf("%d ", topresec[i]); // } int cnt = 1; while (q--) { int L, R; scanf("%d%d", &L, &R); LL ans = 0; for (int i = L; i <= R - 1; ++i) { int be = max(L - 1, topre[i]); int en = min(R, tonext[i]); int pos = lower_bound(tdis + 1, tdis + 1 + n - 1, dis[i]) - tdis; if (vis[pos] == cnt) { be = max(L - 1, topresec[i]); } vis[pos] = cnt; ans += (LL)dis[i] * (i - be) * (en - i); } cnt++; printf("%I64d ", ans); } } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }
2017年3月9日 17:10:38
现在 回顾起这题,当时的写法确实有点复杂,其实要解决那个bug,只需要维护toNext[i]表示大于dis[i]这个数字的第一个位置。toPre[i]表示不小于dis[i]这个数字的位置即可。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <assert.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> #include <bitset> const int maxn = 100000 + 20; int a[maxn]; int dis[maxn]; int n, q; struct Stack { int val, id; }st[maxn]; int toNext[maxn], toPre[maxn]; void init() { for (int i = 1; i <= n - 1; ++i) { dis[i] = abs(a[i + 1] - a[i]); } dis[n] = inf; int top = 0; for (int i = 1; i <= n; ++i) { while (top >= 1 && dis[i] > st[top].val) { toNext[st[top].id] = i; --top; } ++top; st[top].val = dis[i]; st[top].id = i; } top = 0; dis[0] = inf; for (int i = n - 1; i >= 0; --i) { while (top >= 1 && dis[i] >= st[top].val) { toPre[st[top].id] = i; --top; } ++top; st[top].val = dis[i]; st[top].id = i; } } LL calc(int be, int en) { LL ans = 0; for (int i = be; i <= en; ++i) { int L = max(be, toPre[i] + 1); int R = min(en, toNext[i] - 1); ans += 1LL * dis[i] * (i - L + 1) * (R - i + 1); } return ans; } void work() { scanf("%d%d", &n, &q); for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); } init(); // for (int i = 1; i <= n - 1; ++i) { // cout << toNext[i] << " "; // } // for (int i = 1; i <= n - 1; ++i) { // cout << toPre[i] << " "; // } // for (int i = 1; i <= n - 1; ++i) { // cout << dis[i] << " "; // } while (q--) { int L, R; scanf("%d%d", &L, &R); printf("%I64d ", calc(L, R - 1)); } } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }