题目大意
给你个大小为
n
的环,环上的每个点上有一个高度
n≤106
ai≤109
解题思路
方法一
首先环上的情况很难处理,那么考虑取走
ai
最大的位置,断环成链。因为我们取走的是最大值,所以不会有点对
(i,j)
以包含
i
点的路径作为判断是否能看到的依据,唯一的可能就是最大值有多个的时候,这种情况下,经过不包含
那么怎么统计链上的情况。为了不算重,考虑用右端点统计答案,问题转化为固定有右端点
方法二:
一种奇怪的方法:
当然考试的时候我并不是这样想的,我的思想是,用容斥的思想,找总左边路径贡献答案的点对数加右边答案贡献的点对数,减去两边路径贡献的点对数就是答案。两边路径动能贡献的很好求,之间找出最大值和次大值统计一下就可以了。现在考虑统计左边路径贡献答案的个数,右边也一样。
不用破环成链,直接把环复制一遍,现在我们就是要找一个区间
[i,i+n]
内满足条件的对数,最后除2,就是贡献。对于一个位置
i
,显然右边第一个数是可以的,这个要特殊讨论一下。右边第一个大于
程序
方法一:
//ddddddpppppp
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
typedef long long ll;
const int N=1100000;
int T,n,stack[N],tot[N],a[N],b[N],bz[N];
int read()
{
int ret=0;
char c;
while (c=getchar(),c<'0'||c>'9');
ret=c-'0';
while (c=getchar(),c>='0'&&c<='9') ret=ret*10+c-'0';
return ret;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
int mx=0;
fo(i,1,n) a[i]=read(),bz[i]=0,mx=max(mx,a[i]);
fo(i,1,n)
if (a[i]==mx)
{
fo(j,1,n-i) b[j]=a[j+i];
fo(j,n-i+1,n-1) b[j]=a[j-n+i];
break;
}
ll ans=0;
stack[0]=0;
fo(i,1,n-1)
{
while (stack[0]&&stack[stack[0]]<b[i])
{
ans+=tot[stack[0]];
stack[0]--;
}
if (stack[0]&&stack[stack[0]]==b[i])
{
ans+=tot[stack[0]];
if (stack[0]-1) ans++;
tot[stack[0]]++;
}
else
{
if (stack[0]) ans++;
stack[++stack[0]]=b[i];
tot[stack[0]]=1;
}
}
mx=0;
fo(i,1,n-1)
if (b[i]>=mx)
{
mx=b[i];
if (!bz[i]) ans++;
bz[i]=1;
}
mx=0;
fd(i,n-1,1)
if (b[i]>=mx)
{
mx=b[i];
if (!bz[i]) ans++;
bz[i]=1;
}
printf("%lld\n",ans);
}
return 0;
}
方法二(没加优化)
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 2e6 + 5;
struct Node {
int num, side;
Node (int a, int b) {num = a, side = b;}
Node () {}
};
Node f[MAXN * 4], d[MAXN];
int n, m, a[MAXN], l[MAXN], lg[MAXN], rmq[MAXN * 2][22], t1[MAXN], t2[MAXN];
void read(int &x) {
char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
x = 0;
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}
void prepare() {
int top = 0;
memset(t1, 0, sizeof t1), memset(t2, 0, sizeof t2);
for (int i = 1; i <= m; i ++) {
while (top && a[i] >= d[top].num) t1[d[top --].side] = i;
d[++ top] = Node(a[i], i);
}
top = 0;
for (int i = 1; i <= m; i ++) {
while (top && a[i] > d[top].num) t2[d[top --].side] = i;
d[++ top] = Node(a[i], i);
}
}
LL work() {
prepare();
int l = MAXN * 2 + 1, r = MAXN * 2;
LL ans = 0;
for (int i = 1; i <= n; i ++) {
int lim = t2[i];
if (lim == 0 || lim > i + n - 1) lim = i + n - 1;
if (lim == i + 1) {
ans ++;
l = min(l + 1, r + 1);
continue;
}
int side = i + 1;
int top = 0;
bool flag = 0;
while (side != 0 && side <= lim) {
if (!flag) {
if (l <= r && f[l].side != side) {
d[++ top] = Node(a[side], side);
side = t1[side];
} else {
if (l > r) f[++ r] = Node(a[side], side);
side = t1[f[r].side];
flag = 1;
}
} else {
f[++ r] = Node(a[side], side);
side = t1[side];
}
}
for (int j = top; j; j --) f[-- l] = d[j];
int lx = l, rx = r, add = 0;
while (lx <= rx) {
int mid = (lx + rx) >> 1;
if (f[mid].side <= lim) add = mid, lx = mid + 1; else
rx = mid - 1;
}
ans += (add - l + 1);
l = min(l + 1, r + 1);
}
return ans;
}
void solve() {
scanf("%d", &n);
m = n;
for (int i = 1; i <= n; i ++) read(a[i]);
for (int i = 1; i <= n; i ++) a[++ m] = a[i];
LL ans = work();
for (int i = 1; i <= m / 2; i ++) swap(a[i], a[m - i + 1]);
ans += work();
int mx = 0;
for (int i = 1; i <= n; i ++) mx = max(a[i], mx);
int num = 0;
for (int i = 1; i <= n; i ++)
if (a[i] == mx) num ++;
ans /= 2;
if (num > 1) ans -= 1ll * num * (num - 1) / 2; else {
int mxx = 0;
for (int i = 1; i <= n; i ++) {
if (a[i] == mx) continue;
mxx = max(a[i], mxx);
}
int numm = 0;
for (int i = 1; i <= n; i ++) if (a[i] == mxx) numm ++;
ans -= numm;
}
printf("%lld\n", ans);
}
int main() {
int t;
scanf("%d", &t);
for (int i = 1; i <= t; i ++) solve();
}