思路
对于每一对答案贡献((i,j)),假定(a[i] leq a[j]),那么我们考虑枚举每一位(i)去计算其贡献。
对于每一个(i),令(l[i])为左边第一个大于(a[i])的下标,(r[i])为右边第一个大于(a[i])的下标,(cnt[i])表示从(a[j]=a[i](i+1 leq j leq r[i]))的个数。
既然每一个数都要考虑左边比他的,右边比他的情况。考虑把环拆成链可以拆成第一个数是最大值的情况,那么设置边界(n+1)个数也是最大值的情况即可。
维护(l[i],r[i],cnt[i])的一个过程用单调栈维护即可。
然后对于每一个下标(i),其都有一个贡献值(cnt[i]),若(a[i])不是最大值,那么其对答案的贡献+2,如果发现他的(r[i])和(l[i])表示的是同一个位置即(l[i]=1,r[i]=n+1),那么说明有重复的,需要-1.
最后考虑最大值即可,若最大值有x个,那么贡献就是(C_{x}^{2})。
代码
/*人一我百,人十我万*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define int LL
typedef pair<int, LL> PIL;
typedef pair<int, int> PII;
typedef pair<int, double> PID;
typedef unsigned long long ULL;
#define x first
#define y second
const int N = 2e6 + 10, M = 1e5 + 10;
const double PI = acos(-1.0);
const double eps = 1e-5;
const int mod = 1000000007;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
#define gcd __gcd
int a[N], l[N], r[N], b[N];
LL cnt[N];
stack<int> s;
void solve() {
int n;
scanf("%lld", &n);
int mx = 0, pos = 0;
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
if(mx < a[i]) {
mx = a[i];
pos = i;
}
}
for(int i = 1; i <= n; i++) {
b[i] = a[pos++];
if(pos > n) pos = 1;
}
b[++n] = mx;
int sum = 0;
for(int i = 1; i <= n; i++) {
while(!s.empty() && b[s.top()] <= b[i]) s.pop();
if(!s.empty()) l[i] = s.top();
s.push(i);
}
while(!s.empty()) s.pop();
for(int i = n; i >= 1; i--) {
while(!s.empty() && b[s.top()] <= b[i]) {
if(b[s.top()] == b[i] && i <= n && b[s.top()] != mx) cnt[i] = cnt[s.top()] + 1;
s.pop();
}
if(!s.empty()) r[i] = s.top();
s.push(i);
}
LL res = 0;
for(int i = 1; i < n; i++) {
res += cnt[i];
if(b[i] < b[1]) {
res += 2;
if(l[i] == 1 && r[i] == n) res--;
}
}
for(int i = 1; i < n; i++) {
if(mx == a[i]) sum++;
}
res += 1LL * sum * (sum - 1) / 2;
printf("%lld
", res);
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
// int t; cin >> t; while(t--)
solve();
return 0;
}
/*
10
7 3 6 8 1 7 8 4 9 7
*/