[机房测试]简单的打击
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);
}