CF-5E.Bindian Signalizing(单调栈)
思路
对于每一对答案贡献\((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\n", 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
*/