鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6701
題意:求區間內數各不相同並且max(al,al+1,…,ar)−(r−l+1)≤k的區間的個數。
思路:當時讀題忽略了區間內數各不相同這個條件,想着不就是單調棧加簡單的組合數學嗎?後來,寫完樣例不對才發現。。。。然後就想求出某一個數爲終點的最長不重複區間和一某個數爲起點的最長不重複區間,再和單調棧的求出的範圍取個較大、較小值不就找出該數的作用區間了嗎?後來想明白了,以他爲終點和以他爲起點的區間合起來不一定數不重複啊!然後就卡在怎麼快速求包含一個數的最長不重複區間。然後,就沒有然後了。。。。看了別人的思路,直接預處理出以某一個數爲終點的最長不重複區間的L和一某個數爲起點的最長不重複區間的端點R,然後直接枚舉單調棧求出最大值左右區間較小的那一塊求。如果就這麼看的覺得時間太多,但其實均攤爲nlong(n),爲啥咱也不知道。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e5+10;
const int maxm=3e5+10;
int l[N],r[N],L[N],R[N],sta[N],top,pos1,pos2;
int n,a[N],k,x;
int pre[N],suf[N];
ll ans,len;
void getL()
{
for(int i=1;i<=n;i++) pre[i]=0;
L[0]=0;
for(int i=1;i<=n;i++)
{
L[i]=max(pre[a[i]]+1,L[i-1]);
pre[a[i]]=i;
}
}
void getR()
{
for(int i=1;i<=n;i++) suf[i]=n+1;
R[n+1]=n+1;
for(int i=n;i>=1;i--)
{
R[i]=min(suf[a[i]]-1,R[i+1]);
suf[a[i]]=i;
}
}
void getl()
{
top=0;
for(int i=1;i<=n;i++)
{
while(top&&a[sta[top]]<=a[i])
top--;
if(top) l[i]=sta[top]+1;
else l[i]=1;
sta[++top]=i;
}
}
void getr()
{
top=0;
for(int i=n;i>=1;i--)
{
while(top&&a[sta[top]]<=a[i])
top--;
if(top) r[i]=sta[top]-1;
else r[i]=n;
sta[++top]=i;
}
}
int main(void)
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
ans=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
getL();
getR();
getl();
getr();
for(int i=1;i<=n;i++)
{
x=a[i]-k;
//取左右兩邊個數少的來枚舉
if(i-l[i]<r[i]-i)
{
//枚舉區間左端點
for(int j=l[i];j<=i;j++)
{
//第一個可選的右端點,區間內必須要有i
pos1=max(i,j+x-1);
//最後一個可選的右端點
pos2=min(r[i],R[j]);
if(pos2>=pos1) ans+=pos2-pos1+1;
}
}
else
{
//枚舉區間右端點
for(int j=i;j<=r[i];j++)
{
//最後一個可選的左端點
pos1=max(l[i],L[j]);
//第一個可選的左端點,區間內必須要有i
pos2=min(i,j-x+1);
if(pos2>=pos1) ans+=pos2-pos1+1;
}
}
}
printf("%lld\n",ans);
}
return 0;
}