小 L 与 GCD 题解(数学 hard)

题目链接

题目思路

直接看官方的题解吧链接

利用容斥的思想

还有一个就是最后为什么可以直接暴力dfs的结论很妙

还有(p[a[i]]*=(i+1))的原因

感觉像很多巧妙的trick结合变成一个hard的题目

分开每一个小问题可能都会但是结合就gg

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e7+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,m,k,gd;
unsigned a[maxn],ans,b[maxn];
unsigned p[maxn],c[maxn],f[maxn],g[maxn],fp[maxn];
void dfs(int pos,unsigned g,unsigned temp){
    if(k==0){
        printf("%u",ans);
        exit(0);
    }
    if(g==gd){
        ans=ans+temp;
        k--;
    }
    if(pos>m){
        return ;
    }
    for(int i=pos;i<=m;i++){
        dfs(i+1,__gcd(g,a[b[i]]),temp*b[i]);
    }
}
signed main(){
    scanf("%d%d",&n,&k);
    if(n<=30&&(1<<n)<k){
        printf("-1
");
        return 0;
    }
    for(int i=1;i<=1e7;i++){
        p[i]=1;
    }
    for(int i=1;i<=n;i++){
        scanf("%u",&a[i]);
        c[a[i]]++;
        p[a[i]]*=(i+1);
    }
    for(int i=1e7;i>=1;i--){
        for(int j=i;j<=1e7;j+=i){
            f[i]+=c[j];
        }
        if(f[i]==0) continue;
        if(f[i]>30){
            gd=i;
            break;
        }
        g[i]=(1<<f[i]);
        for(int j=i+i;j<=1e7;j+=i){
            g[i]-=g[j];
        }
        g[i]--;
        // 还要减去1 空集的情况
        if(g[i]<k){
            k-=g[i];
        }else{
            gd=i;
            break;
        }
    }
    for(int i=1e7;i>gd;i--){
        if(f[i]==0) continue;
        fp[i]=1;
        for(int j=i;j<=1e7;j+=i){
            fp[i]*=p[j];
        }
        for(int j=i+i;j<=1e7;j+=i){
            fp[i]-=fp[j];
        }
        fp[i]--;
        ans+=fp[i];
    }
    for(int i=1;i<=n;i++){
        if(a[i]%gd==0){
            b[++m]=i;
        }
    }
    dfs(1,0,1);
    return 0;
}


不摆烂了,写题
原文地址:https://www.cnblogs.com/hunxuewangzi/p/15307470.html