题目描述
给定长为 \(n\) 的数列 \(a,b\) ,求数列 \(c\) 满足:
\[c_k=\max_{\gcd(i,j)=k}|a_i-b_j|\\ \]数据范围
- \(1\le n\le 10^5,1\le a_i,b_i\le 10^9\) 。
时间限制 \(\texttt{6s}\) ,空间限制 \(\texttt{256MB}\) 。
分析
别被题目名字带偏了,这道题跟卷积没有一点关系。
如果我们能快速求出 \(c_1\) ,仅保留下标为 \(k\) 的位置,我们就能求出 \(c_k\) 。因此接下来只需考虑 \(c_1\) ,最终复杂度就是求 \(c_1\) 的复杂度套上调和级数的一只 \(\log\) 。
固定 \(i\) ,目标变为对 \(\forall 1\le i\le n\) ,求 \(\max\limits_{\gcd(i,j)=1}b_j\) 和 \(\min\limits_{\gcd(i,j)=1}b_j\) 。后者将 \(a_i,b_i\) 取相反数即可变成前者,因此接下来只需计算 \(\max\limits_{\gcd(i,j)=1}b_j\) 。
看到 \(\gcd\) 基本上就要去想莫比乌斯反演,但 \(\max\) 没有可减性。
二分答案将 \(\max\) 转化为求和,只需求 \(f(i)=\sum\limits_{\gcd(i,j)=1}[b_j\gt mid]\) 。
由于要对所有 \(i\) 一起求答案,所以需要整体二分。
记 \(S=\{j\mid b_j\gt mid\}\) ,则:
\[f(i)=\sum_{j\in S}[\gcd(i,j)=1]=\sum_{j\in S}\sum_{d|i,d|j}\mu(d)\\ \]枚举 \(j\in S\) 的因子 \(d\) ,将 \(\mu(d)\) 的贡献加入桶中,计算 \(f(i)\) 的贡献时只需枚举 \(i\) 的因子即可。
代码实现时,二分判定结束优先递归左边,因为 \(mid\) 减小时 \(S\) 中的元素依然会产生贡献,撤销以后再递归右边。
分析一下时间复杂度,整体二分的每一层我们要枚举 \(j\) 的因子维护桶,再枚举 \(i\) 的因子统计答案。
枚举因子的代价为 \(\mathcal O(n\log n)\) ,离散化后整体二分共有 \(\log n\) 层,再加上最外层调和级数带来的一只 \(\log\) ,时间复杂度 \(\mathcal O(n\log^3n)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5,inf=1e9;
int n,cnt;
int a[maxn],b[maxn],c[2*maxn],p[maxn],buc[maxn],res[maxn];
int mu[maxn],ta[maxn],tb[maxn],tc[maxn];
vector<int> vec[maxn];
void init(int n)
{
bitset<maxn> b;
mu[1]=1;
for(int i=2,cnt=0;i<=n;i++)
{
if(!b[i]) p[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*p[j]<=n;j++)
{
b[i*p[j]]=1;
if(i%p[j]==0) break;
mu[i*p[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i) vec[j].push_back(i);
}
void solve(int l,int r,vector<int> q,vector<int> s)
{
if(q.empty()) return ;
if(l==r)
{
for(auto i:q) tc[i]=l;
return ;
}
int mid=(l+r)>>1;
vector<int> q1,q2,s1,s2;
for(auto i:s)
if(tb[i]>mid)
{
s2.push_back(i);
for(auto d:vec[i]) buc[d]+=mu[d];
}
else s1.push_back(i);
for(auto i:q)
{
int cur=0;
for(auto d:vec[i]) cur+=buc[d];
cur?q2.push_back(i):q1.push_back(i);
}
solve(l,mid,q1,s1);
for(auto i:s2) for(auto d:vec[i]) buc[d]-=mu[d];
solve(mid+1,r,q2,s2);
}
int work(int n)
{
vector<int> vec(n);
iota(vec.begin(),vec.end(),1);
solve(1,cnt,vec,vec);
int res=0;
for(int i=1;i<=n;i++) res=max(res,c[tc[i]]-c[ta[i]]);
return res;
}
int main()
{
scanf("%d",&n),init(n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),c[++cnt]=a[i];
for(int i=1;i<=n;i++) scanf("%d",&b[i]),c[++cnt]=b[i];
sort(c+1,c+cnt+1),cnt=unique(c+1,c+cnt+1)-c-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(c+1,c+cnt+1,a[i])-c,b[i]=lower_bound(c+1,c+cnt+1,b[i])-c;
for(int x=0;x<=1;x++)
{
if(x)
{
reverse(c+1,c+cnt+1);
for(int i=1;i<=cnt;i++) c[i]=inf-c[i];
for(int i=1;i<=n;i++) a[i]=cnt+1-a[i],b[i]=cnt+1-b[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;i*j<=n;j++) ta[j]=a[i*j],tb[j]=b[i*j];
res[i]=max(res[i],work(n/i));
}
}
for(int i=1;i<=n;i++) printf("%d ",res[i]);
return 0;
}
标签:gcd,int,题解,mid,CF102354B,maxn,vec,auto,Yet
From: https://www.cnblogs.com/peiwenjun/p/18505344