HDU 5875 Function(单调栈+在线倍增法)
来源:互联网 发布:java 自定义函数 编辑:程序博客网 时间:2024/05/16 14:22
Description
一个长度为n的序列Ai,m次查询,每次查询求f(l,r),其定义如下:
Input
第一行一整数T表示用例组数,每组用例首先输入一整数n表示序列长度,之后n个整数Ai表示该序列,然后输入一整数m表示查询数,最后m行每行两个整数l,r表示一次查询(1<=n<=1e5,1<=Ai<=1e9,1<=l<=r<=n)
Output
对于每次查询,输出f(l,r)的值
Sample Input
1
3
2 3 3
1
1 3
Sample Output
2
Solution
首先说一个模运算性质:如果y<=x,那么x%y < x/2. 证明如下:
若y<=x/2,x%y < y<=x/2,结论成立;
若x/2 < y<=x,x%y<=x-y < x/2,结论成立.
f(l,r)本质上就是求A[l]连续模A[l+1],…,A[r]的结果,考虑到模一个较小数之后再模大数没有意义,如果对于每个l我们能够处理出以l起始的一个不增序列来让A[l]模的话,那么根据上面提到的模运算性质,这个序列长度至多log A[l](模log A[l]个数后答案是0就不需要继续模了),也就是说,如果能够处理出这不增序列,每次二分查找第一个不大于当前结果的模数,至多log A[l]次二分就可以得到f(l,r)的值,下面来解决如何处理出这些序列
如果对于每个起点i我们都把这个序列存起来显然内存太大,但是如果我们反过来看这个问题,对于每个数,它前面第一个不比它小的数是唯一的,如果把这种关系看作一条边的话,关系图就变成了一个森林,如果在第n+1个点放一个-1的话就变成了一棵树(A[i]>=1),树上任一节点i到根节点的简单路径就是我们需要的以i开始的不增序列,所以问题变成如果求一个数前面第一个不比它小的(即一个数后面第一个不比它大的)以及如果在树上路径二分查找
第一个问题可以用单调栈解决,第二个问题用在线倍增,每次跳到第一个小于等于当前结果的祖先节点的复杂度是O(logn),最多跳log A次,故总复杂度O(mlognlogA)
Code
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<vector>using namespace std;typedef long long ll;#define maxn 111111 #define M 18vector<int>g[maxn];int fa[maxn][M];int val[maxn];void add(int u,int v){ g[v].push_back(u);}int r[maxn],sta[maxn],vis[maxn];void monotonic_stack(int *a,int n){ for(int i=1;i<=n;i++)r[i]=i; int p=0; for(int i=1;i<=n;i++) { if(!p||a[i]>=a[sta[p]]) sta[++p]=i; else { while(p&&a[i]<a[sta[p]]) r[sta[p]]=i,p--; sta[++p]=i; } } while(p)r[sta[p]]=n,p--;}int p[maxn][M];void dfs(int u,int fa){ for(int i=1;i<M;i++)p[u][i]=p[p[u][i-1]][i-1]; for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(v!=fa) { p[v][0]=u; dfs(v,u); } }}void init(int root){ p[root][0]=root; dfs(root,root);}int main(){ int T,n,m,ll,rr; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&val[i]); val[n+1]=-1; for(int i=1;i<=n+1;i++)g[i].clear(); monotonic_stack(val,n+1); memset(vis,0,sizeof(vis)); for(int i=1;i<=n+1;i++) { if(!vis[i]) { vis[i]=1; int j=r[i]; if(i==j)continue; add(i,j); while(!vis[j]&&j<=n) add(j,r[j]),vis[j]=1,j=r[j]; } } init(n+1); scanf("%d",&m); while(m--) { scanf("%d%d",&ll,&rr); int ans=val[ll],pos=ll; while(1) { while(1) { int i; for(i=M-1;i>=0;i--) if(val[p[pos][i]]>ans) break; if(i!=-1)pos=p[p[pos][i]][0]; else pos=p[pos][0]; if(pos>rr)break; if(val[pos]<=ans) { ans%=val[pos]; break; } } if(pos>rr)break; } printf("%d\n",ans); } } return 0;}
- HDU 5875 Function(单调栈+在线倍增法)
- [HDU 5875] Function (单调栈/(RMQ+二分))
- hdu 5875 Function 单调栈 + 暴力
- HDU 5875 Function (单调栈+暴力)
- Hdu-5875 Function(树上倍增st算法)
- HDU 6115 Factory(在线倍增LCA)
- HDU-5662 YJQQQAQ and the function (枚举&&单调栈)
- HDU 5726--GCD【倍增】【单调栈】【STL-map】
- 在线LCA倍增法
- HDU 6109 数据分割(并查集+set+在线倍增法)
- HDU 5449 Robot Dog(树形DP+在线倍增LCA)
- HDU 6110 路径交(线段树+在线倍增LCA)
- 倍增(在线)求LCA
- [HDU 5726] GCD (倍增法+二分)
- HDU-6107 Typesetting(倍增法)
- BZOJ 4568 幸运数字(在线倍增法+线性基)
- HDU 5875 Function 【倍增】 (2016 ACM/ICPC Asia Regional Dalian Online)
- hdu 5875 单调栈+离线
- vim实用技巧
- 并查集(递推法)+路径压缩(转)
- Java之数据流DataInput(Output)Stream 和 字节数组流 ByteArrayInput(Output) Stream的嵌套
- 例题课本例题3-1转换==整数从大到小排序
- 初步进行JDBC的步骤
- HDU 5875 Function(单调栈+在线倍增法)
- Swift 3.0 重大版本发生了哪些变化,各位看官请
- 《Java编程思想第四版》笔记---14章 附:包装类--装箱和拆箱
- 二分查找
- 64. Minimum Path Sum DP经典问题
- 创新科技,只为尊重音乐原声:dFiM睿妙深度剖析
- Java之wait()/sleep()和notify()/notifyAll()
- java 链表实现多项式加法!
- TabBar 的selecedImage无法显示设置图片问题 和item文字颜色选中设置