P9401 [POI 2020/2021 R3] Kolekcjoner Bajtemonów 2

P9401 [POI 2020/2021 R3] Kolekcjoner Bajtemonów 2

题目描述

给定 \(n\) 个数对,在每个数对中选一个数,最大化所选的 \(n\) 个数的最大公约数。

思路

先看数据范围 \(1\le a\le 5\times 10^5\)\(1\le b<2^{63}\)
发现 \(b_i\)\(a_i\) 大很多,且如果要选择一个 \(a_i\) 那么最终的答案一定 \(1\le ans \le 5\times 10^5\)
所以考虑先预处理出所有 \(b_i\) 的最大公约数,再枚举所有可能的 \(ans\),通过最终答案来选择将一些 \(b_i\) 替换为 \(a_i\)
具体地:
对于 \(ans\mid a_i\) 直接选 \(a_i\);对于 \(ans\nmid a_i\) 就只能选 \(b_i\)

如何进行维护和优化:

  1. 考虑基于值域进行处理。令 \(X_{a_i} = b_i\),此时更新答案需要求 \(X\)\(\frac{n}{ans}\) 个区间的 \(b_i\) 的 GCD。用 ST 表维护区间 GCD,每次暴力查询判断答案是否合法。
  2. 如果使用循环递归求 GCD 其常数十分大会被卡飞,请使用二进制求 GCD

复杂度分析:

  1. ST 表预处理 GCD 单次查询 \(O(\log{b})\)
  2. 总查询次数为调和级数 \(O(\sum_{i=1}^n \frac{n}{i}) = O(n\ln{n})\)

总复杂度 \(O(n\ln{n}\log{b})\)

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<climits>
#include<cstdlib>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T&x){
	int w = 0;x = 0;
	char ch = getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') w = 1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9'){
		x = (x<<1)+(x<<3)+(ch^48);
		ch = getchar();
	}
	if(w) x = ~x+1;
}
template <typename T,typename...Args>
inline void read(T&t,Args&...args){
	read(t);read(args...);
}
template <typename T>
inline T Min(T x,T y){ return (x < y ? x : y); }
template <typename T>
inline T Max(T x,T y){ return (x > y ? x : y); }
template <typename T>
inline T Abs(T x){ return (x < 0 ? ~x+1 : x); }
const int N = 5e5+10,LG = 20;
int n;
ll st[N][LG],a[N],ans;
inline ll Gcd(ll x,ll y){
	if(!x || !y) return x|y;
	ll xz = __builtin_ctzll(x);
	ll yz = __builtin_ctzll(y);
	ll z = Min(xz,yz);
	y >>= yz;
	while(x){
		x >>= xz;
		ll diff = x-y;
		xz = __builtin_ctzll(diff);
		y = Min(x,y);
		x = Abs(diff);
	}
	return y << z;
}
inline ll query(int l,int r){
	int k = __lg(r-l+1);
	return Gcd(st[l][k],st[r-(1<<k)+1][k]);
}
int main(){
	// freopen("in.in","r",stdin);
    // freopen("out.out","w",stdout);

	read(n);
	for(int i=1;i<=n;++i){
		int x; ll y; read(x,y);
		a[x] = Gcd(a[x],y); // a一样的合并
		ans = Gcd(ans,y); // 全选b
	}
	for(int i=1;i<=N-10;++i) st[i][0] = a[i];
	for(int k=1;k<=__lg(N-10);++k){
		for(int l=1;l+(1<<k)<=N-10+1;++l){
			st[l][k] = Gcd(st[l][k-1],st[l+(1<<k-1)][k-1]);
		}
	}
	if(ans>N-10){
		printf("%lld",ans);
		return 0;
	}
	for(int i=N-10;i>ans;--i){ // 枚举答案
		ll tmp = 0;
		for(int j=1;i*j<=N-10 && !(tmp%i);++j) tmp = Gcd(tmp,query(i*(j-1)+1,i*j-1));
		if((N-10)%i) tmp = Gcd(tmp,query((N-10)-(N-10)%i+1,(N-10)));
		if(!(tmp%i)){
			ans = i;
			break;
		}
	}
	printf("%lld",ans);

	// fclose(stdin);
	// fclose(stdout);
	return 0;
}
posted @ 2025-04-24 17:14  Tmbcan  阅读(35)  评论(0)    收藏  举报