1. 程式人生 > >HDU 5875 Function(單調棧+線上倍增法)

HDU 5875 Function(單調棧+線上倍增法)

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 18
vector<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; }