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;}
0 0