Codeforces-689 D Friends and Subsequences(RMQ+二分)
阿新 • • 發佈:2018-12-23
題目大意:
給兩個序列a,b 問有多少個相同的區間 在這個區間內a序列的最大值等於b序列的最小值
分析:
首先要解決區間問題 當然用RMQ就行 預處理O(n*log(n)) 詢問O(1) 然後就是要找有多少個區間滿足要求了
直接暴力顯然不可以 分析一下區間特性 一個區間分以下三種情況
1、a的 max > b 的min 這時候應該減小這個區間 這樣max才有可能變小,min才有可能變大
2、a的 max < b 的min 這時候應該增大這個區間 這樣max才有可能變大,min才有可能變小
3、a的 max == b 的min 這時候就無所謂了 增大或者減小區間都可以 只要max還等於min就行
根據以上三條 我們可以採取固定左區間斷點 二分找右區間端點 找一個a的 max == b 的min的最大端點 再找一個
a的 max == b 的min的最小端點 然後這個最大與最小端點之間的所有值都可以和左端點構成一個合法區間 然後累加
以下最大端點和最小端點的差就是答案
AC程式碼:
#include <bits/stdc++.h> #define mset(a,x) memset(a,x,sizeof(a)) typedef long long LL; using namespace std; LL maxn[200005][25]; LL minn[200005][25]; LL a[200005],b[200005]; int n; void init(){ for (int j=1;j<20;j++){ for (int i=1;i+(1<<j)<=n+1;i++){ maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]); minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]); } } } LL rmqmax(int l,int r){ int k=log(r-l+1)/log(2); return max(maxn[l][k],maxn[r-(1<<k)+1][k]); } LL rmqmin(int l,int r){ int k=log(r-l+1)/log(2); return min(minn[l][k],minn[r-(1<<k)+1][k]); } int main (){ while (scanf ("%d",&n)!=EOF){ mset(a,0);mset(b,0);mset(maxn,0);mset(minn,0); for (int i=1;i<=n;i++) scanf ("%lld",&a[i]),maxn[i][0]=a[i]; for (int j=1;j<=n;j++) scanf ("%lld",&b[j]),minn[j][0]=b[j]; init(); int l,r; LL sum=0; for (int i=1;i<=n;i++){ int tl,tr; tl=i,tr=n; int indexr=0,indexl=0; while (tl<=tr){// 求最大右端點 int mid=(tl+tr)/2; LL tmax=rmqmax(i,mid); LL tmin=rmqmin(i,mid); if(tmin==tmax) tl=mid+1,indexr=mid; else if (tmin>tmax) tl=mid+1; else tr=mid-1; } if (indexr) { tl=i,tr=n; while (tl<=tr){// 求最小右端點 int mid=(tl+tr)/2; LL tmax=rmqmax(i,mid); LL tmin=rmqmin(i,mid); if(tmin==tmax) tr=mid-1,indexl=mid; else if (tmin>tmax) tl=mid+1; else tr=mid-1; } sum+=indexr-indexl+1; } } printf ("%lld\n",sum); } return 0; }