小 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\n");
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;
}
不摆烂了,写题