CF 1285 F Classical? 题解

89 篇文章 1 订阅

题目传送门
首先 l c m ( a , b ) = a ∗ b gcd ⁡ ( a , b ) lcm(a,b)=\frac{a*b}{\gcd(a,b)} lcm(a,b)=gcd(a,b)ab,因为 a i ≤ 1 0 5 a_i\leq 10^5 ai105,所以我们可以枚举 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b)
如果当前枚举的 gcd ⁡ ( a , b ) = k \gcd(a,b)=k gcd(a,b)=k,那么问题就变成了 gcd ⁡ ( a i , a j ) = k \gcd(a_i,a_j)=k gcd(ai,aj)=k a i , a j a_i,a_j ai,aj中, a i ∗ a j a_i*a_j aiaj最大为多少。
首先我们可以预处理出对于k所有 k ∣ a i k|a_i kai a i a_i ai,假设这个集合为 m u l k mul_k mulk,那么问题就可以变为求 max ⁡ { x ∗ y } , ( x , y ∈ m u l k , gcd ⁡ ( x , y ) = 1 ) \max \{x*y\},(x,y\in mul_k,\gcd (x,y)=1) max{xy},(x,ymulk,gcd(x,y)=1)
假设有一个集合 S S S,且知道早 S S S i i i的倍数为 c n t i cnt_i cnti。那么怎么求出在 S S S中于 X X X互质的数的个数呢?
这是一个经典的容斥问题:
A n s = ∑ i ∣ X μ ( i ) ∗ c n t i Ans=\sum_{i|X} \mu(i) *cnt_i Ans=iXμ(i)cnti
但是知道这个还不够,这样判断起来最差也会退化成 O ( n 2 ) O(n^2) O(n2)的。
那怎么优化呢?
我们可以先把 m u l k mul_k mulk里的元素降序排序假设是 s 1 , s 2 . . s k s_1,s_2..s_k s1,s2..sk那么如果 gcd ⁡ ( a i , a j ) = 1 , ( i < j ) \gcd(a_i,a_j)=1,(i<j) gcd(ai,aj)=1,(i<j)的化,你要比较 a i ∗ a j a_i*a_j aiaj于答案那个更优。假设有一个更优的答案 a i ′ ∗ a j ′ a_{i'}*a_{j'} aiaj i < i ′ i<i' i<i,则 j ′ 一 定 < j j'一定<j j<j。很明显,可以用栈来优化,最终复杂度 O ( ∑ i = 1 n σ 0 ( i ) 2 ) O(\sum\limits_{i=1}^{n} \sigma_{0}(i)^2) O(i=1nσ0(i)2)
Code:

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int n;
int a[100007],Mobius[100007];
vector<int> mul[100000+7];
bool prime[100000+87];
vector<int> fact[100000+3],facts[100000+3];
int cnt[100000+3];
void add(int x){
	for(auto it:facts[x]){
		cnt[it]++;
	}
}
void erase(int x){
	for(auto it:facts[x]){
		cnt[it]--;
	}
}
int main(){
	fastio;
	R(n);
	rb(i,1,n)
		R(a[i]);
	rb(i,2,100000){
		if(!prime[i]){
			fact[i].PB(i);
			for(int j=i+i;j<=100000;j+=i){
				fact[j].PB(i);
				prime[j]=1;
			}
		}
	}
	rb(i,1,100000){
		for(int j=i;j<=100000;j+=i){
			facts[j].PB(i);
		}
	}
	Mobius[1]=1;
	rb(i,2,100000){
		Mobius[i]=1;
		for(auto it:fact[i]){
			if((i/it)%it==0){
				Mobius[i]=0;
			}
			Mobius[i]*=-1;
		}
	}
	stack<int> sta;
	rb(i,1,n){
		for(int j=1;j*j<=a[i];j++){
			if(a[i]%j==0){
				mul[j].PB(a[i]/j);
				if(j*j!=a[i]) mul[a[i]/j].PB(j);
			}
		}
	}
	LL rest=1; 
	rb(i,1,100000){
		sort(ALL(mul[i]));
		reverse(ALL(mul[i]));
//		if(i==3) for(auto it:mul[i]){
//			cout<<it<<" ";
//		}
//		if(i==3)
//		cout<<endl;
		stack<int> st;
		LL tmp=0;
		for(auto it:mul[i]){
			int tmpp=0;
			for(auto it2:facts[it]){
				tmpp+=Mobius[it2]*cnt[it2];
//				cout<<it2<<" "<<Mobius[it2]<<" "<<cnt[it2]<<" "<<tmpp<<endl; 
			}
//			cout<<it<<"_"<<tmpp<<" "<<cnt[11]<<" "<<Mobius[77]<<endl; 
			if(tmpp){
				while(!st.empty()){
					if(__gcd(st.top(),it)==1){
						if(tmpp==1) break;
						tmpp--;
					}
					erase(st.top());
					st.pop();
				}
				tmp=max(tmp,1ll*st.top()*it);
			}
			add(it);
			st.push(it);
		}
		while(!st.empty()){
			erase(st.top());
			st.pop();
		}
//		if(tmp){
//			cout<<i<<" "<<tmp<<endl; 
//		}
		rest=max(rest,tmp*i);
	}
	cout<<rest<<endl;
	return 0;	
}

/*
41
2 30 1 1 3 1 1 21 27 12 1 1 18 2 1 15 1 9 3 15 15 3 1 1 6 2 2 1 2 12 18 18 2 1 30 1 15 1 2 30 15
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值