題面
題意
給出一個有n個數的排列,現在請你選擇兩個不重疊的區間,使這兩個區間的數的集合可以組成一個公差爲1的等差數列,問有幾個這樣的集合。
做法
我們可以用f[l,r]表示[l,r]中的所有數在給出排列中最少分成幾段,這樣問題就轉化爲了求滿足的區間個數。
可以考慮枚舉區間的右端點,用線段樹維護-的值,這樣當後,假設數r這一個數一個區間,那麼就相當於對線段樹上的區間,如果r在排列中左邊的數t比它小,則可以發現區間-都,而右邊的數也同理,這樣只要對線段樹維護區間加,區間查詢1,2的個數和(就是維護最小值,最小值的個數,次小值,次小值的個數)操作即可。
代碼
#include<bits/stdc++.h>
#define ll long long
#define P pair<ll,ll>
#define mp make_pair
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define N 300100
using namespace std;
ll n,ans,tt,num[N],pos[N];
P gg[5];
struct Node
{
ll ls,rs,dw;
P mn,m2;
void add(ll u){dw+=u,mn.fi+=u,m2.fi+=u;}
}node[N<<1];
inline void up(ll now)
{
ll i,L=node[now].ls,R=node[now].rs;
gg[0]=node[L].mn,gg[1]=node[L].m2;
gg[2]=node[R].mn,gg[3]=node[R].m2;
sort(gg,gg+4);
node[now].mn=gg[0];
for(i=1;i<4;i++)
{
if(gg[i].fi!=gg[0].fi) break;
node[now].mn.se+=gg[i].se;
}
if(i<4)
{
node[now].m2=gg[i];
for(i++;i<4;i++)
{
if(gg[i].fi!=node[now].m2.fi) break;
node[now].m2.se+=gg[i].se;
}
}
else node[now].m2=mp(INF,0);
}
inline void down(ll now)
{
ll L=node[now].ls,R=node[now].rs;
if(node[now].dw)
{
node[L].add(node[now].dw);
node[R].add(node[now].dw);
node[now].dw=0;
}
}
void build(ll now,ll l,ll r)
{
if(l==r)
{
node[now].mn=mp(0,1);
node[now].m2=mp(INF,0);
return;
}
ll mid=((l+r)>>1);
node[now].ls=++tt;
build(tt,l,mid);
node[now].rs=++tt;
build(tt,mid+1,r);
up(now);
}
void add(ll now,ll l,ll r,ll u,ll v,ll w)
{
if(u<=l&&r<=v)
{
node[now].add(w);
return;
}
down(now);
ll mid=((l+r)>>1);
if(u<=mid) add(node[now].ls,l,mid,u,v,w);
if(mid<v) add(node[now].rs,mid+1,r,u,v,w);
up(now);
}
ll ask(ll now,ll l,ll r,ll u,ll v)
{
if(u<=l&&r<=v)
{
ll res=0;
if(node[now].mn.fi<=2) res+=node[now].mn.se;
if(node[now].m2.fi<=2) res+=node[now].m2.se;
return res;
}
down(now);
ll res=0,mid=((l+r)>>1);
if(u<=mid) res+=ask(node[now].ls,l,mid,u,v);
if(mid<v) res+=ask(node[now].rs,mid+1,r,u,v);
return res;
}
int main()
{
ll i,j;
cin>>n;
for(i=1;i<=n;i++)
{
scanf("%lld",&num[i]);
pos[num[i]]=i;
}
build(tt=1,1,n);
for(i=1;i<=n;i++)
{
add(1,1,n,1,i,1);
if(pos[i]>1&&num[pos[i]-1]<i) add(1,1,n,1,num[pos[i]-1],-1);
if(pos[i]<n&&num[pos[i]+1]<i) add(1,1,n,1,num[pos[i]+1],-1);
if(i>1) ans+=ask(1,1,n,1,i-1);
}
cout<<ans;
}