CF1148G

首先把所有数质因数指数设为1,不影响答案。
 
考虑原图G的补图,设f[i]表示给定n个数中有f[i]个数是i的倍数。于是对于每个点,我们可以用容斥原理计算它的度数(即原数组中与它gcd=1的数的个数。比如1号点权值为6,则有deg[1]=n-(f[2]+f[3]-f[6])。
 
如果可以找到点F,使deg[F]>1,设与其没有边相连的两个点为G,H。把以上三个点删除后分两种情况讨论。
 
A:有至少k-3个点的度数>0,二分找到最小的m,使在前m个点中构成的图中,仍然有k-3个点度数>0。大力分类讨论,删除部分仅与m号点有边的点,如果需要的话把F,G,H加入答案中即可(具体见代码)。
 
B:度数>0的点没有k-3个,因为2k<=n,所以至少有k个点度数为0,选取其中任意k个点即可(其权值两两gcd>1)。
 
如果找不到点F,则图中连通块的大小均小于3,在每个连通块中选取一个点加入答案中即可。
 
代码:
```cpp
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k,t;
int pcnt=0,pri[100000];
bool isp[1000000];
void getprime(void)
{
 memset(isp,1,sizeof(isp));
 for(int i=2;i<1000000;i++)
  if(isp[i]){
   pri[pcnt++]=i;
   for(int j=2;i*j<1000000;j++)
    isp[i*j]=0;
  }
 return;
}
struct num{
 int fac[10],cnt;
}arr[100000];
int f[10000000];
void add(int i,int pos,int S)
{
 if(pos==arr[i].cnt){
  if(S!=1) f[S]++;
  return;
 }
 add(i,pos+1,S);
 add(i,pos+1,S*arr[i].fac[pos]);
 return;
}
void del(int i,int pos,int S)
{
 if(pos==arr[i].cnt){
  if(S!=1) f[S]--;
  return;
 }
 del(i,pos+1,S);
 del(i,pos+1,S*arr[i].fac[pos]);
 return;
}
void read(void)
{
 scanf("%d%d",&n,&k);
 for(int i=0;i<n;i++){
  scanf("%d",&t);
  arr[i].cnt=0;
  for(int j=0;pri[j]*pri[j]<=t;j++)
   if(t%pri[j]==0){
    arr[i].fac[arr[i].cnt++]=pri[j];
    while(t%pri[j]==0)
     t/=pri[j];
   }
  if(t>1) arr[i].fac[arr[i].cnt++]=t;
  if(i<2*k) add(i,0,1);
 }
 n=2*k;
 return;
}
int deg[100000],F=-1,G=-1,H=-1;
int getdeg(int i,int pos,int S,int opt)
{
 if(pos==arr[i].cnt)
  return opt*f[S];
 return getdeg(i,pos+1,S,opt)+getdeg(i,pos+1,S*arr[i].fac[pos],-opt);
}
inline bool common(int a,int b)
{
 int i=0,j=0;
 while(i<arr[a].cnt&&j<arr[b].cnt){
  if(arr[a].fac[i]==arr[b].fac[j])
   return 1;
  if(arr[a].fac[i]>arr[b].fac[j])
   j++;
  else i++;
 }
 return 0;
}
void getFGH(int i)
{
 F=i;
 for(int j=0;j<n;j++)
  if(!common(i,j)){
   if(G==-1) G=j;
   else if(H==-1) H=j;
  }
 return;
}
void solve1(void)
{
 for(int i=0;i<n;i++)
  del(i,0,1);
 int cnt=0;
 for(int i=0;i<n&&cnt<k;i++)
  if(getdeg(i,0,1,-1)==cnt){
   cnt++;
   printf("%d ",i+1);
   add(i,0,1);
  }
 return;
}
int check(int m)
{
 int m0=m+1;
 for(int i=0;i<=m;i++)
  if(i!=F&&i!=G&&i!=H)
   add(i,0,1);
  else m0--;
 int x=0;
 for(int i=0;i<=m;i++){
  if(i==F||i==G||i==H)
   continue;
  deg[i]=m0-getdeg(i,0,1,-1);
  if(deg[i]>0)
   x++;
 }
 return x;
}
void solve2(void)
{
 for(int i=0;i<n;i++)
  if(i!=F&&i!=G&&i!=H)
   del(i,0,1);
 int l=k-4,r=n-2,m;
 while(l<r){
  m=(l+r+1)>>1;
  if(check(m)>=k-3)
   r=m-1;
  else l=m;
  for(int i=0;i<=m;i++)
   if(i!=F&&i!=G&&i!=H)
    del(i,0,1);
 }
 m=l+1;
 
 int p=check(m);
 if(p>=k){
  for(int i=0;i<m&&p>k;i++)
   if(i!=F&&i!=G&&i!=H&&deg[i]==1&&!common(i,m)){
    p--; deg[i]=-1;
   }
 }
 else{
  for(int i=0;i<m&&p>k-2;i++)
   if(i!=F&&i!=G&&i!=H&&deg[i]==1&&!common(i,m)){
    p--; deg[i]=-1;
   }
  printf("%d %d ",F+1,G+1);
  if(p==k-3)
   printf("%d ",H+1);
 }
 for(int i=0;i<=m;i++)
  if(i!=F&&i!=G&&i!=H&&deg[i]>0)
   printf("%d ",i+1);
 return;
}
int main(void)
{
 getprime();
 read();
 int w=0;
 for(int i=0;i<n;i++){
  deg[i]=getdeg(i,0,1,-1);
  if(deg[i]<n) w++;
  if(F==-1&&deg[i]<=n-2)
   getFGH(i);
 }
 if(F==-1){
  solve1();
  return 0;
 }
 for(int i=0;i<n;i++)
  del(i,0,1);
 if(check(n-1)<k-3){
  for(int i=0;i<n&&k>0;i++)
   if(deg[i]==0){
    printf("%d ",i+1);
    k--;
   }
 }
 else solve2();
 return 0;
}
```

猜你喜欢

转载自www.cnblogs.com/2005lz/p/12690612.html