【HDU - 5869】Different GCD Subarray Query(思维,数学,gcd,离线处理,查询区间不同数,树状数组 或 二分RMQ)
題干:
This is a simple problem. The teacher gives Bob a list of problems about GCD (Greatest Common Divisor). After studying some of them, Bob thinks that GCD is so interesting. One day, he comes up with a new problem about GCD. Easy as it looks, Bob cannot figure it out himself. Now he turns to you for help, and here is the problem:?
???
??Given an array?aa?of?NN?positive integers?a1,a2,?aN?1,aNa1,a2,?aN?1,aN; a subarray of?aa?is defined as a continuous interval between?a1a1?and?aNaN. In other words,?ai,ai+1,?,aj?1,ajai,ai+1,?,aj?1,aj?is a subarray of?aa, for?1≤i≤j≤N1≤i≤j≤N. For a query in the form?(L,R)(L,R), tell the number of different GCDs contributed by all subarrays of the interval?[L,R][L,R].?
??
Input
There are several tests, process till the end of input.?
???
??For each test, the first line consists of two integers?NN?and?QQ, denoting the length of the array and the number of queries, respectively.?NN?positive integers are listed in the second line, followed by?QQ?lines each containing two integers?L,RL,R?for a query.?
You can assume that?
???
????1≤N,Q≤1000001≤N,Q≤100000?
?????
???1≤ai≤10000001≤ai≤1000000
Output
For each query, output the answer in one line.
Sample Input
5 3 1 3 4 6 9 3 5 2 5 1 5Sample Output
6 6 6題目大意:
給定一個長度n的序列, m個詢問區間[L, R], 問區間內的所有子區間的不同GCD值有多少種.
解題報告:
首先一個套路結論就是gcd值下降非常快,最多log次就降為1,所以以每個數為右端點,往前掃的左端點的gcd肯定不會很多,所以直接暴力維護每個點為右端點,的所有gcd值的出現的最右位置(因為隨著左端點的左移,gcd是單調不增的,所以只需要記錄每個gcd出現的最右位置)。然后直接做區間查詢不同數的個數就可以了。在線可以主席樹,離線可以樹狀數組。
當然這題也可以不這樣預處理,而是用類似RMQ的方法直接處理區間gcd,然后每次二分的往前找這個位置就可以了。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define FF first #define SS second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 1e6 + 5; int n,q; struct Node {int l,r,id; } Q[100005]; bool cmp(Node a,Node b) {return a.r < b.r; } vector<PII> vv[MAX];//gcd值和對應的位置 int a[MAX]; int pos[MAX]; int ans[MAX]; int c[MAX]; void update(int x,int val) {while(x < MAX) {c[x] += val;x += x&-x;} } int sum(int x) {int res = 0;while(x>0) {res += c[x];x -= x&-x;}return res; } int main() {while(cin>>n>>q) {for(int i = 1; i<=n; i++) scanf("%d",a+i),vv[i].clear();memset(pos,0,sizeof pos);memset(c,0,sizeof c);for(int i = 1; i<=n; i++) {int up = vv[i-1].size(),tmp = a[i],now=a[i];vv[i].pb(pm(a[i],i));for(int j = 0; j<up; j++) {now = __gcd(now,vv[i-1][j].FF);if(now == tmp) continue;tmp = now;vv[i].pb(pm(now,vv[i-1][j].SS));}} for(int i = 1; i<=q; i++) scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].id = i;sort(Q+1,Q+q+1,cmp);int cur=1;for(int i = 1; i<=q; i++) {while(cur <= Q[i].r) {int up = vv[cur].size();for(int j = 0; j<up; j++) {if(pos[vv[cur][j].FF]) update(pos[vv[cur][j].FF],-1);update(vv[cur][j].SS,1);pos[vv[cur][j].FF] = vv[cur][j].SS;} cur++;}ans[Q[i].id] = sum(Q[i].r) - sum(Q[i].l - 1);}for(int i = 1; i<=q; i++) printf("%d\n",ans[i]); }return 0 ; }貼一個題解:
感覺很像線段樹/樹狀數組. 因為有很多題都是枚舉從小到大處理查詢的r. 這樣的話就只需要維護[1,i]的情況最開始用的set記錄生成的gcd然后遞推, 超時了.
因為區間gcd是有單調性的.? (i-1->1)和i區間gcd是遞減的.
而且用RMQ可以O(1)的查詢[i,j]gcd的值.如果枚舉[1,i-1]感覺很麻煩.所以用二分跳過中間gcd值相同的部分,即查詢與i的區間gcd值為x的最左邊端點.因為要求不同的值, 這讓我想到了用線段樹求[l,r]中不同數的個數(忘了是哪道題了 zz)
就i而言,首先找出最靠近i的位置使gcd的值為x. 然后和以前的位置作比較. 盡可能的維護這個位置靠右.
總結
以上是生活随笔為你收集整理的【HDU - 5869】Different GCD Subarray Query(思维,数学,gcd,离线处理,查询区间不同数,树状数组 或 二分RMQ)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【FZU - 2254】英语考试(最小生
- 下一篇: 【HDU - 5492】Find a p