题目描述 传送门
按照老师的思路:倍增(st表)+递推 完成的此题。
但不知道是不是我没写好还是我思路不好,这份代码在BZOJ明显比别人的慢。
希望dalao能分享更好的解法。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=3e5+5;
int n;
int a[maxn],begin[maxn],end[maxn],d[maxn][20],f[maxn][20];
long long dp[maxn];
inline int rmq(int l,int r,int o){
int k=0;
while(1<<(k+1)<=r-l+1) k++;
return o?max(f[l][k],f[r-(1<<k)+1][k]):min(d[l][k],d[r-(1<<k)+1][k]);
}
int main(){
int t;
cin>>t;
while(t--){
scanf("%d",&n);
memset(begin,0,sizeof(begin));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(!begin[a[i]]) begin[a[i]]=end[a[i]]=i;
else end[a[i]]=i;
}
for(int i=1;i<=n;i++) d[i][0]=begin[a[i]],f[i][0]=end[a[i]];
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++){
d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
dp[n+1]=0;
long long ans=0;
for(int i=n;i>0;i--){
if(i!=begin[a[i]]) dp[i]=0;
else{
int L=begin[a[i]],R=end[a[i]];
bool ok=1;
for(;;){ //找到以i为起点的最短可选区间[i,R]
if(rmq(L,R,0)<i) {ok=0;break;}
int k=rmq(L,R,1);
if(k>R){
L=R+1;
R=k;
}
else break;
}
if(ok){
if(i==1&&(R==n||dp[R+1]>0)) ans--; //减去全选的
dp[i]=dp[R+1]+1; //从后往前递推
}
}
}
for(int i=1;i<=n;i++) ans+=dp[i];
printf("%lld\n",ans+1);
}
return 0;
}