Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1758 Accepted Submission(s): 849
题意:
给出一个序列,求这个序列里面有多少组,满足组内的最大值-最小值<K
刚开始的想法
设dp[i]表示以i为左端点,最远可以到达的位置,使得区间[i,i],[i,i+1],,,[i,dp[i]]都满足条件
则以i为左端点有dp[i]-i+1个区间满足条件,再对所有的i,累加dp[i]-i+1,即为答案
问题是:递推不出dp[i]
后来看了题解,可以用rmq+二分求出i最远可以到达的位置,而不是递推。
先用st算法求出任意区间的最大值和最小值的差。
枚举左端点i,二分右端点j,则以i为左端点有j-i+1个区间满足。
累加,即为答案。
注意:最后答案要用long long
1 #include<cstdio> 2 #include<cstring> 3 4 #define MAX(x,y) x>y?x:y; 5 #define MIN(x,y) x<y?x:y; 6 #define LL long long 7 8 using namespace std; 9 10 const int MAXN=100000+10; 11 int a[MAXN]; 12 int min[MAXN][20]; 13 int max[MAXN][20]; 14 15 void init(int N) 16 { 17 for(int i=1;i<=N;i++) 18 { 19 min[i][0]=a[i]; 20 max[i][0]=a[i]; 21 } 22 for(int j=1;(1<<j)<=N;j++) 23 { 24 for(int i=1;(i+(1<<j)-1)<=N;i++) 25 { 26 min[i][j]=MIN(min[i][j-1],min[i+(1<<(j-1))][j-1]); 27 max[i][j]=MAX(max[i][j-1],max[i+(1<<(j-1))][j-1]); 28 } 29 } 30 } 31 32 int query(int l,int r) 33 { 34 int k=0; 35 while((1<<(k+1))<=r-l+1) 36 k++; 37 int x=MAX(max[l][k],max[r-(1<<k)+1][k]); 38 int y=MIN(min[l][k],min[r-(1<<k)+1][k]); 39 return x-y; 40 } 41 42 LL solve(int N,int K) 43 { 44 LL res=0; 45 for(int i=1;i<=N;i++) 46 { 47 int l=i,r=N; 48 while(r-l>1) 49 { 50 int p=(l+r)>>1; 51 if(query(i,p)<K) 52 l=p; 53 else 54 r=p; 55 } 56 if(query(i,r)<K) 57 res+=(LL)(r-i+1); 58 else 59 res+=(LL)(l-i+1); 60 } 61 return res; 62 } 63 64 int main() 65 { 66 int test; 67 scanf("%d",&test); 68 while(test--) 69 { 70 int N,K; 71 scanf("%d%d",&N,&K); 72 for(int i=1;i<=N;i++) 73 { 74 scanf("%d",&a[i]); 75 } 76 init(N); 77 78 printf("%I64d\n",solve(N,K)); 79 } 80 return 0; 81 }