E. Special Segments of Permutation(双指针&分治)

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了E. Special Segments of Permutation(双指针&分治)相关的知识,希望对你有一定的参考价值。

E. Special Segments of Permutation(双指针&分治)

思路1:预处理向左、向右第一个大于 a i a_i ai的数 l [ i ] , r [ i ] l[i],r[i] l[i],r[i]。跑两边单调栈即可。

然后枚举每个点作为最大值向两边扩展,因为是对称的,所以每次只需扫较短的那一半区间,每次减少一半,每个数最多被遍历 l o g n logn logn 次。

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

// Problem: CF1156E Special Segments of Permutation
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1156E
// Memory Limit: 500 MB
// Time Limit: 2000 ms
// Date: 2021-08-26 10:37:41
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=2e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=b;i>=a;--i)
#define ios ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
int n,a[N],p[N],l[N],r[N],s[N],tp;
int main(){
	scanf("%d",&n);
	rep(i,1,n) scanf("%d",&a[i]),p[a[i]]=i;
	rep(i,1,n){
		while(tp&&a[s[tp]]<a[i]) tp--;
		l[i]=s[tp],s[++tp]=i;
	}
	s[tp=0]=n+1;
	per(i,1,n){
		while(tp&&a[s[tp]]<a[i]) tp--;
		r[i]=s[tp],s[++tp]=i;
	}
	int ans=0;
	rep(i,1,n){
		if(i-l[i]<r[i]-i){
			rep(j,l[i]+1,i-1)
				if(p[a[i]-a[j]]>i&&p[a[i]-a[j]]<r[i]) ans++;
		}
		else {
			per(j,i+1,r[i]-1)
				if(p[a[i]-a[j]]>l[i]&&p[a[i]-a[j]]<i) ans++;
		}
	}
	printf("%d\\n",ans);
	return 0;
}

思路2分治,对于不跨越中点的区间直接递归,否则先假设最大值在右边,枚举右端 R R R,同理再假设最大值在左边,枚举左端点 L L L

时间复杂度: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 200006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
#define min( a , b ) ( (a) < (b) ? (a) : (b) )
#define max( a , b ) ( (a) > (b) ? (a) : (b) )
#define P 998244353
typedef long long ll;
int n , m;
int A[MAXN];
long long ans = 0;
set<int> S;
void solve( int l , int r ) {
    if( l == r ) return;
    int mid = l + r >> 1;
    int L = mid + 1 , R = mid + 1 , mxr = 0 , mxl;
    S.clear();
    for( ; R <= r ; ++ R ) {
        mxr = max( mxr , A[R] );
        while( L > l && A[L - 1] < mxr ) -- L , S.insert( A[L] );
        if( S.find( mxr - A[R] ) != S.end() ) ++ ans;
    }
    L = mid , R = mid , mxl = 0;
    S.clear();
    for( ; L >= l ; -- L ) {
        mxl = max( mxl , A[L] );
        while( R < r && A[R + 1] < mxl ) ++ R , S.insert( A[R] );
        if( S.find( mxl - A[L] ) != S.end() ) ++ ans;
    }
    solve( l , mid ) , solve( mid + 1 , r );
}
void solve() {
    cin >> n;
    rep( i , 1 , n ) scanf("%d",A + i);
    solve( 1 , n );
    cout << ans << endl;
}

signed main() {
//    freopen("input","r",stdin);
//    freopen("fuckout","w",stdout);
//    int T;cin >> T;while( T-- ) solve();
    solve();
}

以上是关于E. Special Segments of Permutation(双指针&分治)的主要内容,如果未能解决你的问题,请参考以下文章

CF1156E Special Segments of Permutation

CF1156E Special Segments of Permutation题解瞎搞 单调栈

E. Boring Segments(尺取&线段树)

E. Segments Removal(优先队列&set)

cf1555 E. Boring Segments

Codeforces1555 E. Boring Segments(尺取+线段树)