1 条题解

  • 0
    @ 2025-8-24 22:25:57

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar GW
    嗨!希望你能特别快乐||壶关吧

    搬运于2025-08-24 22:25:57,当前版本为作者最后更新于2024-12-22 16:18:35,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    思路

    1. 首先,若要让不同的数的数量减少,必须要把某个数赶尽杀绝,全部同化成其他数,其代价是数组内该数的出现次数。

    2. 其次,考虑操作的策略,要么改成数组内的其他数(记为方案 11),要么就干脆一劳永逸,改成所有数的公倍数(记为方案 22)。

    3. 然后,再考虑如何在这两种方案中抉择,发现把第一次使用方案 11 时,极有可能没有让不同的数的数量减少,如果已经使用了一次方案 11,那么后续再使用方案 11 是显然比方案 22 只优不劣的。即可发现要么全用方案 11,要么全用方案 22

    4. 之后就好办了,详见代码。

    code

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e6+5;
    int a[N],cnt[N],vis[N],sum[N],n,m,k,x,y,b[N],c[N],d[N],T,len;
    int main()
    {
      ios::sync_with_stdio(0);cin.tie(0);
      cin>>n;
      for(int i=1;i<=n;i++)
      {
        cin>>d[i];
        vis[d[i]]++;//输入并记录每个数的出现次数 
      }
      sort(d+1,d+1+n);
      for(int i=1;i<=n;i++)
      {
        if(d[i]!=d[i-1])
        {
          a[++m]=d[i];
          c[m]=vis[d[i]];//手动去重,并记录每个数改成所有数的最小公倍数的代价 (即其出现次数)
        }
      }
      for(int i=1;i<=m;i++)
      {
        for(int j=a[i]*2;j<=1e6;j+=a[i])//这个东西是nlogn的 ,但我不会证 
        {
          if(vis[j])
          {
            b[++len]=vis[a[i]];//寻找每个元素在数组组中是否有倍数,并记录该元素改成其倍数的代价(即其出现次数) 
            break;
          }
        }
      }
      sort(c+1,c+1+m);
      sort(b+1,b+1+len);//记得排序,毕竟选越小的越优 
      for(int i=1;i<=n;i++)
      {
        sum[i]=sum[i-1]+c[i];cnt[i]=cnt[i-1]+b[i];//前缀和优化 
      }
      int x=0,y=0;
      for(int i=0;i<=n;i++)
      {
        while(x+1<=m&&sum[x+1]<=i) ++x;
        while(y+1<=len&&cnt[y+1]<=i) ++y;//对于每个k,看是变成最小公倍数更优还是变成数列中的其他数更优 
        cout<<(m-max(x-1,y))<<' '; 
      }cout<<'\n';
      return 0;
    } 
    
    • 1

    信息

    ID
    6183
    时间
    3000ms
    内存
    512MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者