官方题解:http://blog.sina.com.cn/duoxiao2015
第一场开始时都还没放暑假。。。队友用我感觉会超时的代码AC了两题,囧。。。
HDOJ5288
题意:给一个序列,求共有多少个找不到任意两个不同数是整除关系的连续子序列,结果mod 1e9+7
思路:见官方题解O(nsqrt(n))的算法,但是没看懂,然后下图是大牛给我的解说,看完就懂了
PS:O(n^2)肯定会超时,但是数据太水,队友用O(n^2)的方法过了,就不贴代码了
HDOJ5289
题意:给一个长度为n的序列,为共有多少给连续子序列中最大值与最小值之差小于k
思路:一看到这题,我就想去用O(nlogn)的RMQ+二分,先RMQ预处理,然后对于每个起始位置二分查找结束位置
PS:赛后想起可以用O(n)单调队列直接扫一遍就可以了
PS2:O(n^2)肯定会超时,但是数据太水,队友居然又用O(n^2)的方法过了!!!
PS3:大白书P198页的RMQ的代码有误,害我RE了几次
以下是我写的RMQ+二分的代码
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> using namespace std; typedef long long LL; const int MAXN=100000+10; const int MOD=1e9+7; int dmax[MAXN][20],dmin[MAXN][20],a[MAXN]; int T,n,kk; //输入外挂 template <class T> inline bool read(T &ret) { char c; int sgn; if(c=getchar(),c==EOF) return 0; //EOF while(c!='-'&&(c<'0'||c>'9')) c=getchar(); sgn=(c=='-')?-1:1; ret=(c=='-')?0:(c-'0'); while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0'); ret*=sgn; return 1; } void RMQ_init(){ for(int i=1;i<=n;i++) dmax[i][0]=a[i]; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]); for(int i=1;i<=n;i++)dmin[i][0]=a[i]; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]); } int RMQmin(int L,int R){ int k=0; while((1<<(k+1))<=R-L+1)k++; return min(dmin[L][k],dmin[R-(1<<k)+1][k]); } int RMQmax(int L,int R){ int k=0; while((1<<(k+1))<=R-L+1)k++; return max(dmax[L][k],dmax[R-(1<<k)+1][k]); } int RMQ(int l,int r){ return RMQmax(l,r)-RMQmin(l,r); } int bSearch(int x){ int l=x,r=n; while(l<=r){ int m=(l+r)>>1; if(RMQ(x,m)<kk)l=m+1; else r=m-1; } return r; } LL work(){ LL res=0; for(int i=1;i<=n;i++){ int j=bSearch(i); res+=j-i+1; } return res; } int main(){ #ifdef DEBUG freopen("CBin.txt","r",stdin); //freopen("CBout.txt","w",stdout); #endif read(T); while(T--){ read(n); read(kk); for (int i=1;i<=n;++i)read(a[i]); RMQ_init(); cout<<work()<<"\n"; } return 0; }