[2017集训队作业自选题#119]众数MAX

题目大意

两个长度为n的序列a和b,将它们分别任意排列,然后令 c[i]=a[i]+b[i] ,使c[i]的众数出现次数尽量多。
值域与n同阶。

做法

不妨令A[i]表示a中i的出现次数,B同理。
令ans[i]表示i作为众数的最多出现次数。
显然的暴力
ans[i]=ij=0min(A[j],B[ij])
还可以把式子变成
ans[i]=nx=1ij=0[A[j]>=x][B[ij]>=x]
不妨设立阈值k,然后先做一遍
ans[i]=kx=1ij=0[A[j]>=x][B[ij]>=x]
可以设 c[i]=[A[i]>=x] d[i]=[B[i]>=x] ,可以发现可以用FFT来计算c和d的卷积。
这里复杂度为O(nk log n)。
然后接下来做一遍
ans[i]=ij=0,A[j]>k,B[ij]>kmin(A[j],B[ij])k
这里复杂度为O(n^2/k^2)。
取k=10比较优秀。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef double db;
const int maxlen=100000*5+10,B=10;
const db pi=acos(-1);
int rev[maxlen];
struct node{
    db x,y;
    friend node operator +(node a,node b){
        node c;
        c.x=a.x+b.x;
        c.y=a.y+b.y;
        return c;
    }
    friend node operator -(node a,node b){
        node c;
        c.x=a.x-b.x;
        c.y=a.y-b.y;
        return c;
    }
    friend node operator *(node a,node b){
        node c;
        c.x=a.x*b.x-a.y*b.y;
        c.y=a.x*b.y+a.y*b.x;
        return c;
    }
};
node w[maxlen],c[maxlen],d[maxlen],tt[maxlen];
int ans[maxlen],a[maxlen],b[maxlen],s1[maxlen],s2[maxlen];
int i,j,k,l,t,n,m,N,mx,len,tot,top;
db ce;
void prepare(){
    fo(i,0,len-1){
        int p=0;
        for(int j=0,tp=i;j<ce;j++,tp/=2) p=(p<<1)+(tp%2);
        rev[i]=p;
    }
    w[0].x=1;w[0].y=0;
    w[1].x=cos(2*pi/len);w[1].y=sin(2*pi/len);
    fo(i,2,len) w[i]=w[i-1]*w[1];
}
void DFT(node *a,int sig){
    int i;
    fo(i,0,len-1) tt[rev[i]]=a[i];
    for(int m=2;m<=len;m*=2){
        int half=m/2,bei=len/m;
        fo(i,0,half-1){
            node wi=sig>0?w[i*bei]:w[len-i*bei];
            for(int j=i;j<len;j+=m){
                node u=tt[j],v=tt[j+half]*wi;
                tt[j]=u+v;
                tt[j+half]=u-v;
            }
        }
    }
    if (sig==-1)
        fo(i,0,len-1) tt[i].x/=len;
    fo(i,0,len-1) a[i]=tt[i];
}
void FFT(){
    int i;
    DFT(c,1);
    DFT(d,1);
    fo(i,0,len-1) c[i]=c[i]*d[i];
    DFT(c,-1);
}
int main(){
    scanf("%d",&n);
    N=n;
    fo(i,1,n){
        scanf("%d",&t);
        N=max(N,t);
        a[t]++;
    }
    fo(i,1,n){
        scanf("%d",&t);
        N=max(N,t);
        b[t]++;
    }
    len=1;
    //N=100000;
    while (len<=N*2) len*=2;
    ce=log(len)/log(2);
    prepare();
    fo(i,1,B){
        fo(j,0,len-1)
            c[j].x=c[j].y=d[j].x=d[j].y=0;
        fo(j,0,len-1){
            c[j].x=(a[j]>=i);
            d[j].x=(b[j]>=i);
        }
        FFT();
        fo(j,0,len-1) ans[j]+=int(round(c[j].x));
    }
    fo(i,0,len-1){
        if (a[i]>B) s1[++tot]=i;
        if (b[i]>B) s2[++top]=i;
    }
    fo(i,1,tot)
        fo(j,1,top)
            ans[s1[i]+s2[j]]+=min(a[s1[i]],b[s2[j]])-B;
    fo(i,0,len-1) mx=max(mx,ans[i]);
    printf("%d\n",mx);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值