CF102354B Yet Another Convolution 題解

peiwenjun發表於2024-10-26

題目描述

給定長為 \(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;
}

相關文章