cf上的原题,地址在:http://codeforces.com/contest/5/problem/E
题意
大意是给你围成了一个环的序列,要求所有的点对(i,j)的数目,满足i < j,且i~j之间没有大于i和j的数,之间是指i+1~j-1或i-1~1~n~j+1。
思路
如果这个序列不是环形的,那么仅仅维护一个非增队列就可以统计答案了,然而对于对于环形序列,队首和队首之前的点还可以通过“另一条路”和当前点配对,对于这一部分答案,如果我们维护下队首和队首之前的数的非降序列,那么答案在非降序列上形成了一个区间,由于后缀最大值单调不增,所以可以这个区间会整体向下移动,这样一来复杂度就是线性的了。
比较巧妙的方法是,将整个序列循环移动下,使得最大值在最前面,那么显然这个最大值在非增队列中就会一直占据队首了,对于第二部分的答案,上面提到的那个“队首和队首之前的数的非降序列”就变成了队首这一个数了,因此只需分类讨论队首是否能通过“另一条路”和当前点配对即可,简单很多。
另外,对于单调队列中相等的数,缩成一个数,同时加一个域表示个数,后续处理起来非常方便。
source
#include <bits/stdc++.h>
#ifdef LOCAL
#include "local.h"
#endif // LOCAL
using namespace std;
const int N = 1e6 + 7;
struct Node {
int val, c;
Node() {}
Node(int val, int c) {
this->val = val;
this->c = c;
}
};
int a[N], n, tq, suf[N];
Node que[N];
int main() {
cin >> n;
for (int i = 0; i < n; i++) scanf("%d", a + i);
rotate(a, max_element(a, a + n), a + n);
suf[n] = 0;
for (int i = n - 1; i >= 0; i--) suf[i] = max(suf[i + 1], a[i]);
long long ans = 0;
for (int i = 0; i < n; i++) {
while (tq && a[i] > que[tq - 1].val) {
ans += que[tq - 1].c;
tq--;
}
if (tq && a[i] == que[tq - 1].val) que[tq - 1].c++;
else que[tq++] = Node(a[i], 1);
if (que[tq - 1].c == 1) {
if (tq > 1) {
ans++;
if (tq > 2 && suf[i + 1] <= a[i]) ans ++;
if (tq == 2 && suf[i + 1] <= a[i]) ans += que[0].c > 1;
}
}
else {
ans += que[tq - 1].c - 1;
if (tq == 2) {
ans++;
if (que[0].c > 1 && suf[i + 1] <= a[i]) ans ++;
}
if (tq > 2) {
ans++;
if (suf[i + 1] <= a[i]) ans ++;
}
}
}
cout << ans << endl;
return 0;
}