• 队内欢乐赛


    2022/4/10 队内对抗赛

    禁忌の魔法

    解法1:

    \(\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}lcm(a_i,a_j)=\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\cfrac{a_ia_j}{\gcd(a_i,a_j)}\)

    但是这样还是没有办法去解决问题。

    \(tot=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\cfrac{a_ia_j}{\gcd(a_i,a_j)}\),则答案\(ans=\cfrac{tot-\sum_{i=1}^{n}a_i}{2}\)

    来化简\(tot\),困难之处在于\(\gcd(a_i,a_j)\)不可分,我们先假设这是一个常量\(k\)

    考虑到\(k\)难以分割,那么是不是可以枚举\(k\),看哪些符合条件?

    \(g_k=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{k=1,k|\gcd(a_i,a_j)}^{mx}\cfrac{a_ia_j}{k}\)

    这里为什么枚举\(k|\gcd(a_i,a_j)\)?因为如果是\(k=\gcd(a_i,a_j)\)那么和枚举一样是不可分的。

    我们看\(k|\gcd(a_i,a_j)\)可以推导出:\(k|a_i,k|a_j\)。进而可以拆分得到:

    \(g_k=\left(\sum\limits_{i=1}^{n}\sum\limits_{k=1,k|a_i}^{mx}\cfrac{a_i}{k}\right)\left(\sum\limits_{j=1}^{n}\sum\limits_{k=1,k|a_j}^{mx}\cfrac{a_j}{k}\right)k\),发现有类似的部分。

    可以设\(f_k=\left(\sum\limits_{i=1}^{n}\sum\limits_{k=1,k|a_i}^{mx}\cfrac{a_i}{k}\right)\),则\(g_k=f_k^2\times k\)

    但是\(g_k\)也不是我们要的答案,因为存在\(qk=\gcd(a_i,a_j),q\ge2\)的情况。

    \(h_k=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[k=\gcd(a_i,a_j)]\cfrac{a_ia_j}{\gcd(a_i,a_j)}\),显然满足\(tot=\sum\limits_{k=1}^{mx}h_k\)

    对于\(g_k\)相当于多加了一部分东西,要把这部分减掉,去化成\(h_k\)

    其实可以写成\(g_k=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[qk=\gcd(a_i,a_j),q\ge1]\cfrac{a_ia_j}{k}\)

    \(\begin{aligned}h_k&=g_k-\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[2k=\gcd(a_i,a_j)]\cfrac{a_ia_j}{\gcd(a_i,a_j)}-\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[3k=\gcd(a_i,a_j)]\cfrac{a_ia_j}{\gcd(a_i,a_j)}-\cdots\\&=g_k-\sum\limits_{q=2,qk\le mx}h_{qk}\times q\end{aligned}\)

    #include<bits/stdc++.h>
    #define LL long long
    #define _(d) while(d(isdigit(ch=getchar())))
    using namespace std;
    const int N=1e5+5;
    int n,a[N],mx;
    LL ans,f[N],g[N];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            mx=max(a[i],mx);
            for(int j=1;j*j<=a[i];j++)
                if(a[i]%j==0){
                    g[j]+=a[i]/j;
                    if(j*j!=a[i])g[a[i]/j]+=j;
                }
        }
        for(int i=1;i<=mx;i++)f[i]=g[i]*g[i]*i;
        for(int i=mx;i;i--){
            for(int j=2;i*j<=mx;j++)
                f[i]=f[i]-f[i*j]*j;
            ans+=f[i];
        }
        for (int i=1;i<=n;i++)
            ans-=a[i];
        ans>>=1;
        cout<<ans<<endl;
        return 0;
    }
    

    解法2:

    我们康到了\(lcm\)
    我们想到了\(gcd\)
    于是我们想到了莫比乌斯函数
    于是我们做完了
    化简一下原题

    \(\sum_{1<=i<=n-1}\sum_{i+1<=j<=n}\frac{a_i*a_j}{gcd(a_i,a_j)}\)

    枚举一手\(gcd\)

    \(\sum_{d}\frac{1}{d}\sum_{1<=i<=n-1}\sum_{i+1<=j<=n}a_i*a_j[d|a_i,d|a_j][gcd(\frac{a_i}{d},\frac{a_j}{d})==1]\)

    根据莫比乌斯函数的性质展开一下

    \(\sum_{d}\frac{1}{d}\sum_{1<=i<=n-1}\sum_{i+1<=j<=n}[d|a_i,d|a_j]\sum_{d'|\frac{a_i}{d},d'|\frac{a_j}{d}}μ(d')\)
    把枚举因数扔到前面去qwq

    \(\sum_{d}\frac{1}{d}\sum_{d'}μ(d')\sum_{1<=i<=n-1,d*d'|a_i}\sum_{1<=j<=n-1,d*d'|a_j}a_ia_j\)
    后面这个显然是关于\(d*d'\)的一个函数,预处理即可
    然后我们就做完了
    预处理的话
    \(\sum a_ia_j=\frac{((\sum a_i)^2-\sum {a_i^2})}{2}\)
    好了 真的做完了x

    #include <bits/stdc++.h>
    using namespace std;
    long long f[1000010],mu[1000010],Prime[1000010],IsPrime[1000010],hs[1000010],cnt,Con[1000010],N,a[1000010];
    void Init(){
        mu[1]=1;
        for (int i=2;i<=1000000;i++){
            if (!IsPrime[i]) Prime[++cnt]=i,mu[i]=-1;
            for (int j=1;i*Prime[j]<=1000000&&j<=cnt;j++){
                IsPrime[i*Prime[j]]=true;
                if (i%Prime[j]==0){
                    mu[i*Prime[j]]=0;
                    break;
                }
                mu[i*Prime[j]]=-mu[i];
            }
        }
    }
    int main(){
        scanf("%lld",&N);
        for (int i=1;i<=N;i++)
            scanf("%lld",&a[i]),Con[a[i]]++;
        Init();
        for (int i=1;i<=1000000;i++)
            for (int j=i;j<=1000000;j+=i)
                hs[i]+=1ll*Con[j]*j,f[i]=f[i]+1ll*Con[j]*j*j;
        for (int i=1;i<=1000000;i++)
            f[i]=(1ll*hs[i]*hs[i]-f[i])>>1ll;
        long long ans=0;
        for (int k=1;k<=1000000;k++)
            for (int T=k;T<=1000000;T+=k)
                ans+=1ll*mu[T/k]*f[T]/k;
        cout<<ans;
        return 0;
    }
    

    姆q的图书馆

    一段连续的gcd,随着区间长度的增加,它的gcd一定是递减的

    而区间长度是递增的

    于是这两个函数如果有交点一定只有一个交点

    于是就对于每一个\(l\)位置,二分找一下是否有令它不合法的右端点

    至于连续gcd的话,线段树随便写写就行了

    如果有的话就存起来

    然后考虑问题的转化

    如果我修改区间中的一个数字为一个没有出现过的素数,那么这个区间的gcd一定是\(1\)

    这样的话问题就变成了 ,

    对于一堆线段,每条线段上都要取其中一个点,问你最少取几个点

    这就是一个经典的贪心问题

    然后题目要的是前缀

    我们离线一下,根据\(r\)排个序

    然后每次做的时候把答案标记在\(r\)上,只有\(r\)右侧是这个答案

    然后扫一遍区间输出即可。

    #include<bits/stdc++.h>
    using namespace std;
    struct Node{
        int l,r;
    }a[200005];
    int Tree[800005],N,cnt,ans[200005];
    int aa[200005];
    bool temp(Node a,Node b){
        return (a.r<b.r); 
    }
    void Build(int nw,int l,int r){
        if (l==r){
            Tree[nw]=aa[l];
            return;
        }
        int mid=(l+r)>>1;
        Build(nw<<1,l,mid);
        Build(nw<<1|1,mid+1,r);
        Tree[nw]=__gcd(Tree[nw<<1],Tree[nw<<1|1]);
        return;
    }
    int query(int Now,int L,int R,int l,int r){
        if (L<=l&&r<=R) return Tree[Now]; 
        int mid=(l+r)>>1;
        if (L<=mid&&mid<R) return __gcd(query(Now<<1,L,R,l,mid),query(Now<<1|1,L,R,mid+1,r));
        else if (L<=mid) return query(Now<<1,L,R,l,mid);
        else if (mid<R) return query(Now<<1|1,L,R,mid+1,r);
    }
    void Pre(){
        for (int i=1;i<=N;i++){
            int l=i,r=N+1;
            while (l<=r){
                int mid=(l+r)>>1;
                if (query(1,i,mid,1,N)>mid-i+1) l=mid+1;
                else if (query(1,i,mid,1,N)==mid-i+1){
                    a[++cnt].r=mid;
                    a[cnt].l=i;
                    break;
                }
                else r=mid-1;
            }
        }
    }
    int main(){
        scanf("%d",&N);
        for (int i=1;i<=N;i++)
            scanf("%d",&aa[i]);
        Build(1,1,N);    
        Pre();
        sort(a+1,a+cnt+1,temp);
        int x=a[1].r,Ans=1;
        ans[a[1].r]=Ans;
        for (int i=1;i<=cnt;i++)
            if (a[i].l<=x) continue;
            else{
                x=a[i].r;
                Ans++;
                ans[a[i].r]=Ans;
        }
        int nw=0;
        for (int i=1;i<=N;i++){
            if (ans[i]!=0&&ans[i]!=nw) nw=ans[i];
            printf("%d ",nw);
        }
        return 0;
    }
    
  • 相关阅读:
    腾讯ios内部视频,什么垃圾视频
    项目中学习ReactiveCocoa的使用方法
    Mac 裁剪mp3
    AFN使用etag进行网络缓存
    Mac 高效 软件
    presentedViewController 和 presentingViewController 以及 dismissViewControllerAnimated 的使用
    Objective-C: NSFileManager 的使用
    UIButton的imageEdgeInsets 和 titleEdgeInsets
    Objective-C :Category
    Objective-C中的@property
  • 原文地址:https://www.cnblogs.com/lightcoder/p/16126781.html
Copyright © 2020-2023  润新知