CF1285F Classical?

Link
\(A=\max(a_i)\)
先枚举\(g\),然后计算满足\(\gcd(a_i,a_j)=g\)的答案。
考虑从大到小枚举集合中的数,假如现在枚举到\(x\),若存在\(y>x\)满足\(\gcd(x,y)=g\),那么所有\((x,y)\)内的数都不会对答案有贡献了。
因此我们可以弹栈直到栈中没有数与\(x\)互质,弹栈的时候计算贡献。
随之而来的问题就是如何计算栈中有多少与\(x\)互质的数,设\(cnt_x\)表示\(x\)的出现次数,考虑经典反演:
\(\sum\limits[\gcd(x,y)=1]cnt_y=\sum\limits_{d|x}\mu(d)\sum\limits_{d|y}cnt_y\)
那么我们维护\(cnt'_x=\sum\limits_{x|y}cnt_y\)即可。
这样做的时间复杂度为\(O(n\log^2n)\)
那么我们最开始将\(a_i\)的所有因数都加入集合,然后只需要枚举\(\gcd(a_i,a_j)=1\)跑一遍即可得到答案。
最后记得特判\(\operatorname{lcm}(a_i,a_i)\)的情况。
时间复杂度为\(O(A\log A)\)

#include<cctype>
#include<cstdio>
#include<vector>
#include<algorithm>
using i64=long long;
const int N=100007;
int vis[N],cnt[N],mu[N];std::vector<int>fac[N],stk;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int main()
{
    int n=read(),m=0;i64 ans=0;mu[1]=1;
    for(int i=1,x;i<=n;++i) m=std::max(m,x=read()),vis[x]=1;
    for(int i=1;i<=m;++i) for(int j=i+i;j<=m;j+=i) mu[j]-=mu[i];
    for(int i=1;i<=m;++i) for(int j=i;j<=m;j+=i) if(vis[i]|=vis[j],mu[i]) fac[j].push_back(i);
    for(int i=m;i;--i)
    {
	int num=0;
	if(!vis[i]) continue;
	for(int j:fac[i]) num+=mu[j]*cnt[j];
        for(;num;stk.pop_back())
	{
	    int last=num;
	    for(int j:fac[stk.back()]) if(--cnt[j],!(i%j)) num-=mu[j];
	    if(last^num) ans=std::max(ans,1ll*i*stk.back());
        }
	for(int j:fac[i]) ++cnt[j];
	stk.push_back(i);
    }
    printf("%lld",std::max(ans,1ll*m));
}
posted @ 2020-05-19 17:32  Shiina_Mashiro  阅读(153)  评论(0编辑  收藏  举报