前言
考試就做起了這一道...還反反覆覆調了很久...
心情複雜/越來越感覺自己好弱
題目
difference.cpp 1S/128M
給出一個包括N個元素的整數數組A,包括A本身在內,共有 (N+1)*N / 2個非空子段。例如:1 3 2的子段爲{1} {3} {2} {1 3} {3 2} {1 3 2}。
在這些子段中,如果最大值同最小值的差異不超過K,則認爲這是一個合格的子段。給出數組A和K,求有多少符合條件的子段。
例如:3 5 7 6 3,K = 2,符合條件的子段包括:{3} {5} {7} {6} {3} {3 5} {5 7} {7 6} {5 7 6},共9個。
Input
第1行:2個數N, K(1 <= N <= 50000, 0 <= K <= 10^9)
第2 - N + 1行:每行1個數,對應數組的元素Ai(0 <= Ai <= 10^9)
Output
輸出符合條件的子段數量。
Sample Input
5 2
3
5
7
6
2
Sample Output
9
分析
這個問題的本質在於找到最大的區間[i, j],保證從該區間的所有子區間都滿足題意,但是如果每找到一個區間[i, j]就通通把它的子區間數加進去,那麼一定會出現重複的情況,所以這裏我們只要維護一邊,然後去查找最大區間,最後再累加j - i + 1即可,代碼中之所以直接累加j - i是因爲此時的最大區間爲[i, j - 1]
說到具體的實現所用的數據結構自然就是雙端隊列,維護一個記錄最小值的單調遞增隊列和一個記錄最大值的單調遞減隊列,最後只需要比較兩個隊列的頭就可以保證[i, j]的合法性
記得刪除隊頭:然後每次i結束前,將隊列中位置<=i的刪掉,因爲對於i+1及後面的數而言,i不能對它們造成影響
代碼
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=50000,INF=0x3f3f3f3f;
ll a[MAXN+5],deq1[MAXN+5],deq2[MAXN+5];
int n,k;
ll Abs(ll a,ll b)
{
if(a-b<0)
return b-a;
return a-b;
}
void Solve()
{
ll l1=1,l2=1,r1=0,r2=0,ans=0,j=1;
for(int i=1;i<=n;i++)
{
//正常維護區間最值
while(j<=n)
{
//Min
while(l1<=r1&&r1>0&&a[deq1[r1]]>=a[j])
r1--;
deq1[++r1]=j;
//Max
while(l2<=r2&&r2>0&&a[deq2[r2]]<=a[j])
r2--;
deq2[++r2]=j;
if(Abs(a[deq1[l1]],a[deq2[l2]])>k)
break;
j++;
}
ans+=j-i;
while(l1<=r1&&deq1[l1]<=i)
l1++;
while(l2<=r2&&deq2[l2]<=i)
l2++;
}
printf("%lld\n",ans);
}
int main()
{
//freopen("difference.in","r",stdin);
//freopen("difference.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
Solve();
return 0;
}