题意:给n个数,然后m个询问,询问是L R,问从L开始一直向后取余的结果
思路:比赛的时候就有个想法,就是因为每个数取余的话只有比它小的才有影响,那么我们就找到比当前位置小的那个数的位置就可以了,中间略过的元素是没有影响的,然后因为一个数取余后结果至多变成一半,所以10的9次方的数最大的复杂度就是log10的9次方,然后找位置的话需要用二分,所以总的复杂度就是m*logn*log1e9,我和队友怎么算复杂度都不应该超时啊,知道今天才看到一个大神的博客,下面就是两个代码的差别,一个T,一个A
这是我的TLE的代码:
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100010;
int A[maxn],n,m,dp2[maxn][17];
inline int getint(){
int res=0;
char c=getchar();
bool mi=false;
while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
return mi ? -res : res;
}
void rmq_init(){
int i,j;
for(i=1;i<=n;i++) dp2[i][0]=A[i];
for(j=1;(1<<j)<=n;j++){
for(i=1;i+(1<<j)-1<=n;i++){
if(dp2[i][j-1]<dp2[i+(1<<(j-1))][j-1])
dp2[i][j]=dp2[i][j-1];
else
dp2[i][j]=dp2[i+(1<<(j-1))][j-1];
}
}
}
int rmq(int x,int y){
int m=ceil(log((double)(y-x+1))/log(2.0));//我的这里的询问是用一个数学公式推出来的
if(dp2[x][m]<dp2[y-(1<<m)+1][m]) return dp2[x][m];
else return dp2[y-(1<<m)+1][m];
}
int slove(int le,int ri){
int i,pos=le+1,now=A[le];
while(1){
if(rmq(pos,ri)>now) break;
int lll=pos,rrr=ri,ppp;
while(rrr>=lll){
int mid=(lll+rrr)>>1;
if(rmq(lll,mid)<=now) rrr=mid-1,ppp=mid;
else lll=mid+1;
}
now=now%A[ppp];pos=ppp+1;
if(pos>ri) break;
if(now==0) break;
}
return now;
}
int main(){
int i,T,l,r;
T=getint();
while(T--){
n=getint();
for(i=1;i<=n;i++) A[i]=getint();
rmq_init();
m=getint();
for(i=0;i<m;i++){
l=getint();r=getint();
if(l==r) printf("%d\n",A[l]);
else printf("%d\n",slove(l,r));
}
}
return 0;
}
这是现在AC的:
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100010;
int A[maxn],n,m,dp2[maxn][20],Log[maxn];
void rmq_init(){
int i,j;
for(i=1;i<=n;i++) dp2[i][0]=A[i];
for(j=1;(1<<j)<=n;j++){
for(i=1;i+(1<<j)-1<=n;i++){
if(dp2[i][j-1]<dp2[i+(1<<(j-1))][j-1])
dp2[i][j]=dp2[i][j-1];
else
dp2[i][j]=dp2[i+(1<<(j-1))][j-1];
}
}
}
int rmq(int x,int y){
int m=Log[y-x+1];//现在的查询无疑做到了O(1),下面的数组计算就是那个数学公式,先预处理的
if(dp2[x][m]<dp2[y-(1<<m)+1][m]) return dp2[x][m];
else return dp2[y-(1<<m)+1][m];
}
int slove(int le,int ri){
int i,pos=le+1,now=A[le];
while(1){
if(rmq(pos,ri)>now) break;
int lll=pos,rrr=ri,ppp;
while(rrr>=lll){
int mid=(lll+rrr)>>1;
if(rmq(lll,mid)<=now) rrr=mid-1,ppp=mid;
else lll=mid+1;
}
now=now%A[ppp];pos=ppp+1;
if(pos>ri) break;
if(now==0) break;
}
return now;
}
int main(){
int i,T,l,r;
Log[0]=-1;for(int i=1;i<maxn;i++) Log[i]=Log[i>>1]+1;//这里就是预处理
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&A[i]);
rmq_init();
scanf("%d",&m);
for(i=0;i<m;i++){
scanf("%d%d",&l,&r);
if(l==r) printf("%d\n",A[l]);
else printf("%d\n",slove(l,r));
}
}
return 0;
}
PS:以后还是要注意,时间可以节省的就节省,你永远不知道哪里就TLE了,但是讲道理的说,上面的代码应该给AC把