题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5289
题面:
Assignment Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 672 Accepted Submission(s): 335
解题:
比赛的时候,怎么想都想不正确。想去找近期的不合法的点,复杂度太高。
看了题解才知道是用ST算法的。先前不知道,这是一篇非常不错的ST算法的介绍。
枚举左边端点,二分右端点。用ST算法推断该区间是否合法,直至右端点到极限(即二分的左右边界相遇或交叉)。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <map> #include <vector> #include <cmath> #include <algorithm> #define mod 1000000007 using namespace std; int t,n,k; int a[100100],minn[100010][20],maxn[100010][20],mid; long long ans; void Rmq_Init() { int m=19; for(int i=1;i<=n;i++) maxn[i][0]=minn[i][0]=a[i]; for(int i=1;i<=m;i++) for(int j=n;j;j--) { maxn[j][i]=maxn[j][i-1]; minn[j][i]=minn[j][i-1]; if(j+(1<<(i-1))<=n) { maxn[j][i]=max(maxn[j][i],maxn[j+(1<<(i-1))][i-1]); minn[j][i]=min(minn[j][i],minn[j+(1<<(i-1))][i-1]); } } } int Query_dif(int l,int r) { int m=floor(log((double)(r-l+1))/log(2.0)); int Max=max(maxn[l][m],maxn[r-(1<<m)+1][m]); int Min=min(minn[l][m],minn[r-(1<<m)+1][m]); return Max-Min; } int solve(int l) { int le,ri; le=l; ri=n; while(le<=ri) { mid=(le+ri)/2; if(Query_dif(l,mid)>=k) { ri=mid-1; } else { le=mid+1; } } /*if(Query_dif(l,mid)>=k) return mid-l; else return mid-l+1;*/ return ri-l+1; } int main() { scanf("%d",&t); while(t--) { ans=0; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } Rmq_Init(); for(int i=1;i<=n;i++) { ans=ans+solve(i); //cout<<i<<" "<<Query_dif(i,n)<<endl; //cout<<ans<<endl; } printf("%lld\n",ans); } return 0; }