HDU

来源:互联网 发布:网络购彩平台贴吧 编辑:程序博客网 时间:2024/05/17 23:44

题目链接


题意:

给出长度为n的正整数序列,q个询问[l,r]

f(l,r) = f(l,r-1)%a[r] l<r

f(l,r) = a[r] l=r

对于每个询问[l,r],输出f(l,r)的值


思路:


可以推出f(l,r)=a[l]%a[l+1]%...%a[r]

然后我们知道,因为是%,所以只有遇见第一个比a[l]小的数,这个%才起作用,然后%完的值在找第一个比他小的...依

类推.



第一种做法: (暴力,毫无疑问,数据再一次出水了.)原本我想的是把数组倒过来做单调栈.


对于每个数都预处理右面第一个比他小的,然后对于每一个查询就这样一直暴力找右面比他小的取模.


PS :其实完全可以出组数据,复杂度就O(n2) 。铁定T;



 一个数x最多只能被比她小的数取模logx次

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int mod=1e9+7;const int maxn=1e5+10;int a[maxn],nxt[maxn];int main(){int _;cin>>_;int n,m;while(_--){scanf("%d",&n);for(int i = 1;i <= n;++i)scanf("%d",&a[i]),nxt[i]=-1;for(int i = 1;i < n ;++i){for(int j=i+1;j<=n;++j){if(a[i]>a[j]){nxt[i]=j;break;}}}int l,r;scanf("%d",&m);while(m--){scanf("%d %d",&l,&r);int ans=a[l];for(int i=nxt[l];i<=r&&i!=-1&&ans;i=nxt[i]){ans %= a[i];}printf("%d\n",ans);}}return 0;}


第二种做法:

我们知道RMQ可以解决区间最值问题,那么对于这个题目我们可以nlogn预处理所有区间的最小值,然后怎么去找

右面第一个比他小的呢?

二分.二分区间,每次优先判断左面的区间最小值是否小于当前要取模的值,每次二分找到第一个比他小的以后,改变

区间左右端点继续二分,知道超出区间或找不到或当前数为0.


 因为一个数一个数x最多只能被比她小的数取模logx次 .所以复杂度 nlog^2n

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int mod=1e9+7;const int maxn=1e5+10;int f[maxn][20],lg2[maxn];int n,m;void RMQ(){for(int i=2;i<=n;++i) lg2[i]=lg2[i/2]+1;for(int i=1;i<=n;++i)scanf("%d",&f[i][0]);for(int j=1;(1<<j) <= n;++j)for(int i=1;i+(1<<j)-1 <= n;++i){f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);}return ;}int RMQ_MIN(int l,int r){int k=lg2[r-l+1];return min(f[l][k],f[r-(1<<k)+1][k]);}int main(){int _;cin>>_;while(_--){scanf("%d",&n);RMQ();scanf("%d",&m);while(m--){int l,r;scanf("%d %d",&l,&r);if(l==r){printf("%d\n",f[l][0]);continue;}int flag=1,ans=f[l][0],L=l+1,R=r;while(flag){while(L<R){int mid = L+R >> 1;if(RMQ_MIN(L,mid)<=ans) {R=mid;}else if(RMQ_MIN(mid+1,R)<=ans){L=mid+1;}else{flag=0;break;}}if(flag)ans %= f[L][0];if(L>=r)break;++L,R=r;}printf("%d\n",ans);}}return 0;}


第三种做法:

线段树.

其实这个就是线段树区间最小值的裸题吧,但是当时你就是想不到.

tree[]存储的是对应区间的最小值,我们对于每次要取模的区间(L,R)。如果tree[]大于要取模的数,那么直接舍去这段.

对于那些满足条件的我们如何找到第一个比他小的值?

这里我们返回的是比取模的值小的最小的下标的那个,也就得到了离他最近的那个.然后也是一直查询.


PS:

这里要返回最小的那个下标,一定要处理到叶子结点,我一开始想错了,还记录了区间里最小值的那个下标,然后到了

这个区间直接返回了....显然不对...

复杂度 nlog^2n

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int mod=1e9+7;const int maxn=1e5+10;int a[maxn],tree[maxn*4];int n,m;void pushup(int d){tree[d] = min(tree[d<<1],tree[d<<1|1]);return ;}void build(int l,int r,int d){if(l==r){tree[d] = a[l];return ;}int mid = l+r >> 1;build(l,mid,d<<1);build(mid+1,r,d<<1|1);pushup(d);return;}int query(int L,int R,int l,int r,int d,int val){if(tree[d] > val) return mod;if(l <= L&&R<=r){if(L==R)return L;int mid =L+R>>1;if(tree[d<<1] <= val)return query(L,mid,l,r,d<<1,val);elsereturn query(mid+1,R,l,r,d<<1|1,val);}int res = mod;int mid = L+R >>1;if(l <= mid){ res = query(L,mid,l,r,d<<1,val); if(res <= r)//当左子树找到满足题意的没必要去找右子树了,因为左子树下标一定比右子树小.  return res;}if(r > mid){res = query(mid+1,R,l,r,d<<1|1,val);if(res <= r)return res;}return res;}int main(){int _;cin>>_;while(_--){scanf("%d",&n);for(int i = 1;i<=n;++i)scanf("%d",&a[i]);build(1,n,1);scanf("%d",&m);while(m--){int l,r;scanf("%d %d",&l,&r);if(l==r){printf("%d\n",a[l]);continue;}int ans = a[l];while(l<r){int pos = query(1,n,l+1,r,1,ans);if(pos<=r)ans %= a[pos];if(ans==0)break;l=pos;}printf("%d\n",ans);}}return 0;}


不同的线段树写法.上面那个比这个快一倍...


#include<bits/stdc++.h>using namespace std;typedef long long ll;const int mod=1e9+7;const int maxn=1e5+10;int a[maxn],tree[maxn*4];int n,m;void pushup(int d){tree[d] = min(tree[d<<1],tree[d<<1|1]);return ;}void build(int l,int r,int d){if(l==r){tree[d] = a[l];return ;}int mid = l+r >> 1;build(l,mid,d<<1);build(mid+1,r,d<<1|1);pushup(d);return;}int query(int L,int R,int l,int r,int d,int val){if(tree[d] > val) return mod;if(L==l&&R==r){if(l==r)return l;int mid =l+r>>1;if(tree[d<<1] <= val)return query(L,mid,l,mid,d<<1,val);elsereturn query(mid+1,R,mid+1,r,d<<1|1,val);}int mid = L+R >>1;if(r <= mid)return query(L,mid,l,r,d<<1,val);else if(l > mid)return query(mid+1,R,l,r,d<<1|1,val);else{int res = mod;res = min(query(L,mid,l,mid,d<<1,val),query(mid+1,R,mid+1,r,d<<1|1,val));return res;}}int main(){int _;cin>>_;while(_--){scanf("%d",&n);for(int i = 1;i<=n;++i)scanf("%d",&a[i]);build(1,n,1);scanf("%d",&m);while(m--){int l,r;scanf("%d %d",&l,&r);if(l==r){printf("%d\n",a[l]);continue;}int ans = a[l];while(l<r){int pos = query(1,n,l+1,r,1,ans);if(pos<=r)ans %= a[pos];if(ans==0)break;l=pos;}printf("%d\n",ans);}}return 0;}

原创粉丝点击