【HDU 5869】Different GCD Subarray Query【區間不同 gcd 個數】

題意:

給出 nn 個數字,一共 qq 次查詢,每次詢問一個 llrr,查詢區間 [l,r][l,r] 中有多少個不同的 gcdgcd,其中一個子區間代表一個 gcdgcd(1n,q105,1ai106)(1\leq n,q\leq 10^5,1\leq a_i\leq 10^6)


思路:

區間查詢不同 gcdgcd 的個數,這類題像一類套路問題,主要要抓住 gcdgcd 的幾個性質。

  • 固定右端點,移動左端點,gcdgcd 的值從 ara_r 不斷變化,要麼不變,要麼至少除 22(因爲 gcdgcd 最小值爲 22)。因此固定右端點之後,只會存在至多 loglog 個不同的 gcdgcd,我們對於相同的 gcdgcd 僅保留最靠右的位置。
  • 因此對於每個右端點,我們記錄一個 vectorvector,存儲對於這個右端點的所有不同的 gcdgcd 值。我們可以根據 vector[i1]vector[i-1] 來更新 vector[i]vector[i]

處理完上述操作之後,我們得到了 nlognn*logn(l,r,gcd)(l,r,gcd) 三元組,然後我們將所有查詢按照右端點排序。

記錄一個 pospos,不斷右移到查詢的右端點位置,每次移動時將 vector[pos]vector[pos] 內的信息存儲到樹狀數組中,即對於每個三元組 (l,pos,gcd)(l,pos,gcd),如果該 gcdgcd 未出現過,則在樹狀數組的 ll 位置 +1+1,並設置 vis[gcd]=lvis[gcd]=l。如果該 gcdgcd 出現過,則比較 ll 是否比 vis[gcd]vis[gcd] 更大,如果更大,則修改 gcdgcd 在樹狀數組中的位置。上述操作即不斷維護 gcdgcd 最靠右的出現位置。然後對於每個查詢,直接在樹狀數組中區間查詢即可。


總結:

此題最關鍵的在於發現區間 gcdgcd 不斷除 22 的性質,然後將查詢離線利用樹狀數組不斷維護每個 gcdgcd 最後出現的位置即可完成。


代碼:

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef double db;
const int N = 1e6+100;
const db EPS = 1e-9;
using namespace std;

void dbg() {cout << "\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}

int n,q,a[N],vis[N];
ll ans[N],c[N];
struct Node{
	int l,r,id;
	bool operator < (Node xx) const {
		return r < xx.r;
	}
}Q[N];
vector<pair<int,int> > base[N];

inline int lowbit(int x) {return x&(~x+1);}
inline void update(int x,ll v) {for(;x<=n;x+=lowbit(x)) c[x] += v;}
inline ll ask(int x){
	ll tp = 0;
	while(x) tp += c[x], x -= lowbit(x);
	return tp;
}

int gcd(int a,int b){
	return b == 0 ? a:gcd(b,a%b);
}

int main()
{
	while(~scanf("%d%d",&n,&q)){
		rep(i,1,n) scanf("%d",&a[i]);
		rep(i,1,n) base[i].clear();
		rep(i,0,n) c[i] = 0;
		//更新每個點的vector
		rep(i,1,n){
			base[i].push_back(make_pair(i,a[i])); vis[a[i]] = 1;
			for(auto &v:base[i-1]){
				int tp = gcd(v.second,a[i]);
				if(!vis[tp]){
					base[i].push_back(make_pair(v.first,tp));
					vis[tp] = 1;
				}
			}
			for(auto &v:base[i])
				vis[v.second] = 0;
		}
		rep(i,1,q) scanf("%d%d",&Q[i].l,&Q[i].r), Q[i].id = i;
		sort(Q+1,Q+1+q);
		int pos = 0;
		rep(i,1,q){
			while(pos <= Q[i].r){
				for(auto &v:base[pos]){
					int hp = v.first;
					if(hp > vis[v.second]){
						if(vis[v.second]) update(vis[v.second],-1);
						vis[v.second] = hp;
						update(vis[v.second],1);
					}
				}
				pos++;
			}
			ans[Q[i].id] = ask(Q[i].r)-ask(Q[i].l-1);
		}
		rep(i,1,q) printf("%lld\n",ans[i]);
		memset(vis,0,sizeof vis);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章