题意:给你一串数a再给你一些区间(lef,rig),求出a[lef]%a[lef+1]...%a[rig]
题解:我们可以发现数字a对数字b取模时:如果a<b,则等于原数,否则a会变小至少一半。就是说a最多成功取模(log2 a)次,所以我们只需要每次在区间内找到最前面一个小于等于a的值,接着更新a与区间左端点,直到没有值比a小或者区间取模完成。
我们可以使用线段树求出区间内小于某个值的最前一个位置,具体方法就是:父节点记录区间最小值,接着当这一整段的最小值小于等于给定的值时就递归进此子树(另一棵子树还是可能递归,因为可能前一个子树包含的区间大于所求的区间),这样我们知道第一次递归到叶子节点时就一定是最前一个小于等于此值的位置(如果有这个值的话)。
//别人的代码,自己还没写 #include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<string> #include<cstdio> #include<cstring> #include<stdlib.h> #include<iostream> #include<algorithm> using namespace std; #define eps 1E-8 /*注意可能会有输出-0.000*/ #define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型 #define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化 #define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0 #define mul(a,b) (a<<b) #define dir(a,b) (a>>b) typedef long long ll; typedef unsigned long long ull; const int Inf=1<<30; const double Pi=acos(-1.0); const int Mod=1e9+7; const int Max=200010<<2; int nnum[Max],cnt,flag; int segtr[Max]; struct node { int mmin,mpos; }res; void Upnow(int now,int next) { segtr[now]=min(segtr[next],segtr[next|1]); return; } void Create(int sta,int enn,int now) { if(sta==enn) { scanf("%d",&segtr[now]); nnum[cnt++]=segtr[now];//记录每个位置的值 return; } int mid=dir(sta+enn,1); int next=mul(now,1); Create(sta,mid,next); Create(mid+1,enn,next|1); Upnow(now,next); return; } void Query(int sta,int enn,int now,int x,int y,int z) { if(sta>=x&&enn<=y) { if(sta==enn)//到叶子节点 { flag=1;//表示只能到一次叶子节点 if(segtr[now]<=z)//找到 { res.mmin=segtr[now]; res.mpos=sta; } return; } if(segtr[now]>z)//这一段不需要再递归 return; } int mid=dir(sta+enn,1); int next=mul(now,1); if(mid>=x&&!flag&&segtr[next]<=z)//之前没到叶子,子树区间最小值小于等于给定的值 Query(sta,mid,next,x,y,z); if(mid<y&&!flag&&segtr[next|1]<=z) Query(mid+1,enn,next|1,x,y,z); return; } int main() { int t,n,m; int lef,rig; scanf("%d",&t); while(t--) { cnt=1; scanf("%d",&n); Create(1,n,1); scanf("%d",&m); for(int i=1;i<=m;++i) { scanf("%d %d",&lef,&rig); if(lef==rig)//只有一个值 { printf("%d ",nnum[lef]); continue; } int ans=nnum[lef]; lef++; while(1) { res.mmin=Inf,res.mpos=-1; flag=0; Query(1,n,1,lef,rig,ans); if(ans>=res.mmin)//成功取模 { ans=ans%res.mmin; lef=res.mpos+1; } else break; if(lef>rig||ans==0)//结束条件 break; } printf("%d ",ans); } } return 0; }
单调栈写法
/*#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> using namespace std; int b[100005*4]; long long a[100005]; int c,l,r; void build(int node,int left,int right) { if(left==right) { scanf("%d",&b[node]); a[c++]=b[node]; return; } build(node*2,left,(left+right)/2); build(node*2+1,(left+right)/2+1,right); b[node]=min(b[node*2],b[node*2+1]); return; } long long query(int x,int y,int left,int right,int node,int val) { if(x==y) { if (b[node]<=val) return left; else return -1; } int mid = (left+right)/2; int ans=-1; if(x<=mid && b[node*2]<=val) { ans=query(x,y,left,mid,node*2,val); return ans; } if(y>mid && b[node*2+1]<=val) { ans=query(x,y,mid+1,right,node*2+1,val); return ans; } return -1; } //以上是自己写的线段树,错误多多 int main() { int T,n,m; long long ans; while(~scanf("%d",&T)) { while(T--) { scanf("%d",&n); c=1; build(1,1,n); scanf("%d",&m); while(m--) { scanf("%d%d",&l,&r); ans=a[l]; while(l<r) { l=query(l+1,r,1,n,1,a[l]); if (l==-1) break; ans=ans%a[l]; } printf("%lld ",ans); } } } return 0; } */ #include<cstdio> #include<iostream> #include<cmath> #include<algorithm> using namespace std; int a[100005],b[100005]; int main() { int T,n,m,l,r; int ans; while(~scanf("%d",&T)) { while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { b[i]=-1; for(int j=i+1;j<=n;j++) if (a[j]<=a[i]) {b[i]=j;break;} } // for(int i=1;i<=n;i++) printf("%d ",b[i]); // printf(" "); scanf("%d",&m); while(m--) { scanf("%d%d",&l,&r); ans=a[l]; while(l<r) { if(b[l]==-1 || b[l]>r) break; ans=ans%a[b[l]]; l=b[l]; } printf("%d ",ans); } } } return 0; }