hdoj6701 Make Rounddog Happy(单调栈维护区间最大+分治)

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6701

题意:

给定一个长度为$n$的数组,问存在多少个子序列可以满足:$max(a_l,a_{l+1},...,a_r-(r-l+1)<=k$。同时要求子序列内数字不重复。

数据范围:

$1<=n<=3e5,1<=a_i<=n$。

分析:

首先考虑不重复怎么做呢?

很容易想到用单调栈维护当前$a_i$作为最大值可以扩展到的左界和右界。$L[i],R[i]$。

然后考虑怎么重复数字呢?然后就可以想到,一个数作为左界(右界)向右(左)不重复数字最长到达的位置。$L2[i],R2[i]$。

然后分治考虑答案。对于每个$a_i$作为最大值,找边界!

拿左侧或者右侧元素较少的一侧进行枚举。

假设左侧元素更少:枚举左界,然后判断右界。同理去处理右界。

然后对于每个情况累加答案即可。

有一个证明复杂度的博客:https://blog.csdn.net/qq_41955236/article/details/99990149

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn=3e5+10;
 5 int a[maxn],L[maxn],L2[maxn],R[maxn],R2[maxn],q[maxn],num[maxn],len[maxn];
 6 int main(){
 7 //    freopen("in.txt","r",stdin);
 8 //    freopen("out.txt","w",stdout);
 9     int t,n,k;scanf("%d",&t);
10     while (t--){
11         scanf("%d%d",&n,&k);
12         for (int i=1;i<=n;i++) scanf("%d",&a[i]),len[i]=max(1,a[i]-k);
13         int top=0;
14         for (int i=1;i<=n;i++){
15             while (top && a[q[top]]<a[i]) top--;
16             if (top==0) L[i]=1; else L[i]=q[top]+1;
17             q[++top]=i;
18         }
19         top=0;
20         for (int i=n;i>=1;i--){
21             while (top && a[q[top]]<a[i]) top--;
22             if (top==0) R[i]=n; else R[i]=q[top]-1;
23             q[++top]=i;
24         }
25         for (int i=1;i<=n;i++) num[i]=0;top=0;
26         for (int i=1;i<=n;i++){
27             while (top<n && num[a[top+1]]==0){
28                 top++; num[a[top]]++;
29             }
30             L2[i]=top;
31             num[a[i]]--;
32         }
33         for (int i=1;i<=n;i++) num[i]=0;top=n+1;
34         for (int i=n;i>=1;i--){
35             while (top>1 && num[a[top-1]]==0){
36                 top--; num[a[top]]++;
37             }
38             R2[i]=top;
39             num[a[i]]--;
40         }
41         ll ans=0;
42         for (int i=1;i<=n;i++){
43             if (i-L[i]+1<=R[i]-i+1){
44                 for (int j=L[i];j<=i;j++){
45                     if (L2[j]<i) continue;
46                     int rr=min(L2[j],R[i]);//右界 
47                     int nn=i-j+1; //a[i]左侧的长度
48                     if (len[i]<=nn) ans+=rr-i+1;
49                     else ans+=max(0,rr-a[i]+k-j+2); 
50                 }
51             }
52             else{
53                 for (int j=i;j<=R[i];j++){
54                     if (R2[j]>i) continue;
55                     int ll=max(R2[j],L[i]);//左界 
56                     int mm=j-i+1; //a[i]右侧的长度
57                     if (len[i]<=mm) ans+=i-ll+1;
58                     else ans+=max(0,j+1-a[i]+k-ll+1);
59                 }
60             }
61         }
62         printf("%lld\n",ans);
63     }
64     return 0;
65 }

 

posted @ 2019-08-22 02:59  Changer-qyz  阅读(117)  评论(0编辑  收藏