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\)。
如何进行维护和优化:
- 考虑基于值域进行处理。令 \(X_{a_i} = b_i\),此时更新答案需要求 \(X\) 中 \(\frac{n}{ans}\) 个区间的 \(b_i\) 的 GCD。用 ST 表维护区间 GCD,每次暴力查询判断答案是否合法。
- 如果使用循环递归求 GCD 其常数十分大会被卡飞,请使用二进制求 GCD。
复杂度分析:
- ST 表预处理 GCD 单次查询 \(O(\log{b})\);
- 总查询次数为调和级数 \(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;
}

浙公网安备 33010602011771号