题目大意
给定一个长度为
n
的环,每个位置有一个高度
当在环上的两条路径中有至少一条路径上除了两个端点,路径上的高度都不大于
min(ai,aj)
时,
i,j
可以互相看到。
求有多少对可以互相看到的点对。
T组数据。
Data Constraint
T≤5,n≤1000000
题解
因为是环,所以可以先copy一遍,转成序列问题。
每个位置可以看到对方,当且仅当这两个位置之间没有比他们高的位置。
所以可以维护一个双向的单调递减队列,每次有元素进入时就计算它对前面的贡献。
至于相同的元素,我们可以把它们压在一起,再搞个链表维护一下。
注意要减去重复的方案,统计一下最大值和最小值的个数就好了。
时间复杂度: O(n)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 2000000 + 10
typedef long long ll ;
struct Note {
int v , w , same , samA , Head ;
} S[N] ;
int a[N] , Next[N] ;
int T , n , tot1 , tot2 , FMax , SMax ;
ll ans ;
inline int Read() {
int ret = 0 ;
char ch = getchar() ;
while ( ch < '0' || ch > '9' ) ch = getchar() ;
while ( ch >= '0' && ch <= '9' ) ret = ret * 10 + ch - '0' , ch = getchar() ;
return ret ;
}
int main() {
freopen( "forever.in" , "r" , stdin ) ;
freopen( "forever.out" , "w" , stdout ) ;
T = Read() ;
while ( T -- ) {
memset( S , 0 , sizeof(S) ) ;
memset( Next , 0 , sizeof(Next) ) ;
FMax = SMax = 0 ;
ans = tot1 = tot2 = 0 ;
n = Read() ;
for (int i = 1 ; i <= n ; i ++ ) {
a[i] = Read() , a[n+i] = a[i] ;
if ( a[i] > FMax ) {
SMax = FMax ;
FMax = a[i] ;
tot2 = tot1 ;
tot1 = 0 ;
} else if ( a[i] > SMax ) SMax = a[i] , tot2 = 0 ;
if ( a[i] == FMax ) tot1 ++ ;
if ( a[i] == SMax ) tot2 ++ ;
}
if ( n < 2 ) { printf( "0\n" ) ; continue ; }
if ( FMax == SMax ) {
ans -= (ll)tot1 * (tot1 - 1) / 2 ;
} else ans -= (ll)tot1 * tot2 ;
int head = 1 , tail = 0 ;
for (int i = 1 ; i <= 2 * n ; i ++ ) {
if ( head <= tail && i - S[head].w >= n ) {
S[head].samA -- ;
if ( S[head].same ) S[head].same -- ;
if ( !S[head].samA ) {
S[head].v = S[head].w = S[head].same = S[head].samA = 0 ;
head ++ ;
} else S[head].w = Next[S[head].w] ;
}
while ( a[i] > S[tail].v && tail >= head ) {
ans += S[tail].same ;
S[tail].v = S[tail].w = S[tail].same = S[tail].samA = 0 ;
tail -- ;
}
if ( tail >= head && S[tail].v == a[i] ) {
ans += S[tail].same ;
S[tail].samA ++ ;
Next[S[tail].Head] = i ;
S[tail].Head = i ;
if ( head < tail && S[tail-1].Head <= n ) ans ++ ;
if ( i <= n ) S[tail].same ++ ;
}
else {
if ( head <= tail && S[tail].Head <= n ) ans ++ ;
tail ++ ;
S[tail].v = a[i] ;
S[tail].w = i ;
S[tail].samA = 1 ;
S[tail].Head = i ;
if ( i <= n ) S[tail].same = 1 ;
}
}
printf( "%lld\n" , ans ) ;
}
return 0 ;
}
以上.