[关闭]
@xiaoziyao 2020-11-21T16:23:35.000000Z 字数 2801 阅读 1369

CF1446D Frequency Problem解题报告

解题报告


CF1446D1 Frequency Problem (Easy Version)&CF1446D2 Frequency Problem (Hard Version)解题报告:

更好的阅读体验

题意

分析

首先有一个结论:最长的众数不唯一的子序列一定包含整个序列的众数

反证法:设整个序列的众数为,如果这个子序列中众数不含有,那么我们尝试每次给这个子序列扩展一个位置(如果扩展不了那么众数一定含有这个),因为为整个序列的众数,每一次扩展会最多让增加,所以总有一个时间的数量会等于当前序列众数的数量,此时这个序列满足条件,且它一定优于原来的序列。

有了这个结论,我们先求得整个序列的众数,如果众数不唯一,那么答案就是,否则我们继续分析:

先考虑Easy Version,因为的值域为,所以我们直接枚举这个众数(设为),然后重构一个新序列

对于一个满足条件的序列,它的出现次数与的出现次数一定相等,因此我们可以统计所有之和为的序列进答案。

但是这里有一个疑问:如果在某一个区间中存在一个数,出现次数比均多,那这样的序列是否合法?

还是可以用上面的那种扩展的方法,发现一定存在一个更长的序列众数为,且比刚才的序列更优,因此刚刚的序列被统计了也不会对最终答案造成影响。

这样我们就用的复杂度解决了Easy Version。

对于Hard Version,观察数据范围,我们可以考虑进行根号分治。

设定一个阈值,对于出现次数大于的数字,它的个数一定不会超过,因此我们可以用一个存下所有出现次数大于的数字,然后用Easy Version的方法暴力统计答案,时间复杂度

然后,对于出现次数小于等于的数字,我们很显然可以枚举这个出现次数(设为),然后对于整个序列做一遍,里面维护所有数的出现次数出现次数的出现次数,只要出现次数的出现次数大于等于,我们就统计这个答案。这样我们可以用的复杂度解决问题。

时间复杂度:

代码

Easy Version:

  1. #include<stdio.h>
  2. #define inf 1000000000
  3. const int maxn=200005,maxk=105;
  4. int n,maxx,cnt,ans;
  5. int a[maxn],tot[maxk],sum[maxn],t[maxn*2];
  6. inline int min(int a,int b){
  7. return a<b? a:b;
  8. }
  9. inline int max(int a,int b){
  10. return a>b? a:b;
  11. }
  12. int main(){
  13. scanf("%d",&n);
  14. for(int i=1;i<=n;i++){
  15. scanf("%d",&a[i]);
  16. tot[a[i]]++;
  17. }
  18. for(int i=1;i<=100;i++){
  19. if(tot[i]==maxx)
  20. cnt++;
  21. if(tot[i]>maxx)
  22. cnt=1,maxx=tot[i];
  23. }
  24. if(cnt>1){
  25. printf("%d\n",n);
  26. return 0;
  27. }
  28. for(int i=1;i<=100;i++)
  29. if(tot[i]==maxx){
  30. maxx=i;
  31. break;
  32. }
  33. for(int i=1;i<=100;i++){
  34. for(int j=1;j<=n;j++)
  35. sum[j]=sum[j-1]+(a[j]==i? -1:(a[j]==maxx? 1:0));
  36. for(int j=-n;j<=n;j++)
  37. t[n+j]=inf;
  38. for(int j=1;j<=n;j++){
  39. ans=max(ans,j-t[n+sum[j]]+1);
  40. t[n+sum[j-1]]=min(t[n+sum[j-1]],j);
  41. }
  42. }
  43. printf("%d\n",ans);
  44. return 0;
  45. }

Hard Version

  1. #include<stdio.h>
  2. #include<math.h>
  3. #include<vector>
  4. #define inf 1000000000
  5. using namespace std;
  6. const int maxn=200005,maxt=505;
  7. int n,maxx,cnt,ans,S,val;
  8. int a[maxn],tot[maxn],sum[maxn],t[maxn*2],cnt1[maxn],cnt2[maxn];
  9. vector<int>v;
  10. inline void modify(int x,int v){
  11. cnt2[cnt1[x]]--;
  12. cnt1[x]+=v;
  13. cnt2[cnt1[x]]++;
  14. }
  15. int main(){
  16. scanf("%d",&n),S=sqrt(n);
  17. for(int i=1;i<=n;i++){
  18. scanf("%d",&a[i]);
  19. tot[a[i]]++;
  20. }
  21. for(int i=1;i<=n;i++){
  22. if(tot[i]==maxx)
  23. cnt++;
  24. if(tot[i]>maxx)
  25. cnt=1,maxx=tot[i];
  26. }
  27. if(cnt>1){
  28. printf("%d\n",n);
  29. return 0;
  30. }
  31. for(int i=1;i<=n;i++){
  32. if(tot[i]==maxx)
  33. val=i;
  34. else if(tot[i]>S)
  35. v.push_back(i);
  36. }
  37. for(int i=0;i<v.size();i++){
  38. int k=v[i];
  39. for(int j=1;j<=n;j++)
  40. sum[j]=sum[j-1]+(a[j]==k? -1:(a[j]==val? 1:0));
  41. for(int j=-n;j<=n;j++)
  42. t[n+j]=inf;
  43. for(int j=1;j<=n;j++){
  44. ans=max(ans,j-t[n+sum[j]]+1);
  45. t[n+sum[j-1]]=min(t[n+sum[j-1]],j);
  46. }
  47. }
  48. for(int i=1;i<=S;i++){
  49. for(int j=1;j<=n;j++)
  50. cnt1[j]=cnt2[j]=0;
  51. int l=1,r=0;
  52. while(r<n){
  53. r++,modify(a[r],1);
  54. while(cnt1[a[r]]>i)
  55. modify(a[l],-1),l++;
  56. if(cnt2[i]>=2)
  57. ans=max(ans,r-l+1);
  58. }
  59. }
  60. printf("%d\n",ans);
  61. return 0;
  62. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注