[机房测试]简单的打击

Description

具体的来说,两把剑分别代表了两个长度为 \(n\) 的序列 \(a,b\)

你什么方面都强,所以你可以分别重新锻造这两把剑,锻造就相当于重新排列这两个序列。

合并这两把剑,让它变成一把新剑(对应序列 \(c\)),合并相当于把对应位置上的数加起来 \(c_i=a_i+b_i\)

最后你准备拿着这把新剑去找大 Boss,造成的伤害是众数出现的次数。

问怎么排列才能使得伤害最大化,输出最大伤害。

Solution

考虑枚举这个众数 \(x\),那么该数最多出现

\[\sum_{i=1}^{x-1} \min\{a_i,b_{x-i}\} \]

看上去很像一个卷积,但是并不能直接处理。

一个转换是

\[\sum_{i=1}^{x-1} \min\{a_i,b_{x-i}\}=\sum_{k} \sum_{i=1}^{x-1} [a_i\geq k][b_{x-i} \geq k] \]

可以枚举 \(k\),然后内层 FFT,但是这样复杂度是 \(O(|V|^2\log |V|)\) 的。会发现很多位置都是零,因为大于等于 \(k\) 的数至多有 \(\lfloor \frac{|V|}{k} \rfloor\) 个。那么可以考虑数据分治:
· 对于 \(k\leq T\),用 FFT
· 对于 \(k> T\),直接把还有值的数提取出来,然后暴力。
实际测出来 \(T=4\)\(T=5\) 的时候,效率比较高。

#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define db double
#define rint register int

const int N=(1<<20)+3;
const db PI=acos(-1);

struct Complex{
    db x,y;
    Complex(db x_=0,db y_=0):x(x_),y(y_){}
    Complex operator +(Complex a){return Complex(x+a.x,y+a.y);}
    Complex operator -(Complex a){return Complex(x-a.x,y-a.y);}
    Complex operator *(Complex a){return Complex(x*a.x-y*a.y,y*a.x+x*a.y);}
}a[N<<1];

int n,m,op,rk[N<<1];

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

void FFT(){
    for(rint i=0;i<n;i++)
        if(i<rk[i]) swap(a[i],a[rk[i]]);
    for(rint p=2;p<=n;p<<=1){
        int len=p>>1;
        Complex w(cos(2*PI/p),op*sin(2*PI/p));
        for(rint k=0;k<n;k+=p){
            Complex now=Complex(1.0,0.0);
            for(rint l=k;l<k+len;l++){
                Complex t=now*a[l+len];
                a[l+len]=a[l]-t;
                a[l]=a[l]+t;
                now=now*w;
            }
        }
    }
}

const int T=4;
int A[N],B[N],ans[N],A_[N],B_[N],an,bn;

int main(){
    freopen("hit.in","r",stdin);
    freopen("hit.out","w",stdout);
    n=read();
    for(rint i=1;i<=n;++i) A[read()]++;
    for(rint i=1;i<=n;i++) B[read()]++;
    for(m=2e5,n=1;n<=m;n<<=1);
    for(int i=0;i<n;i++)
        rk[i]=(rk[i>>1]>>1)|((i&1)? n>>1:0);
    for(int k=1;k<=T;k++){
        for(int i=0;i<n;i++)
            a[i].x=(A[i]>=k),a[i].y=(B[i]>=k);
        op=1,FFT();
        for(rint i=0;i<n;i++) a[i]=a[i]*a[i];
        op=-1,FFT();
        for(int i=0;i<=m;i++)
            ans[i]+=((int)(a[i].y/n/2+0.5));
    }
    for(int i=1;i<=n;i++){
        if(A[i]>T) A_[++an]=i;
        if(B[i]>T) B_[++bn]=i;
    }
    for(int i=1;i<=an;i++)
        for(int j=1;j<=bn;j++)
            ans[A_[i]+B_[j]]+=min(A[A_[i]],B[B_[j]])-T;
    int ret=0;
    for(int i=0;i<n;i++) ret=max(ret,ans[i]);
    printf("%d",ret);
}
posted @ 2021-10-19 21:09  Kreap  阅读(46)  评论(0编辑  收藏  举报