题目描述:小 D 有一些同样长的木棍,然后每个都切成长度分别不超过 (m) 的两段。现在想拼回去,但是小 D 遗失了一部分木棍,而且还忘了它们的长度和个数,所以他想拼接出尽可能多的相同长度的木棍。给出每段小木棍的长度,求尽可能多的木棍个数和此时的木棍最小长度。
数据范围:(2le n,mle 10^5,1le a_ile m)
开桶 ({b_m})
[egin{aligned}
Ans_x&=frac12sum_{i+j=x}min(b_i,b_j) \
&=sum_ksum_{i+j=x}[b_ige k][b_jge k]
end{aligned}
]
使用 FFT,成功得到了一个 (O(nmlog m)) 的比暴力还lj的做法。
但是你发现 (sum b_i=nle 10^5),所以你可以"阈值法"(?),定一个数 (t),当 (kle t) 时用 FFT 计算,(b_i>t) 的数不超过 (frac nt) 个,所以时间复杂度是 (O(tmlog m+(frac nt)^2))。
我不会算函数最值,纯粹喜欢 (t=10) 而已(太草了)。
#include<bits/stdc++.h>
#define Rint register int
using namespace std;
const int N = 1 << 18, B = 10;
const double PI = acos(-1);
int n, m, a[N], b[N], tot, f[N], now, ans;
template<typename T>
inline void read(T &x){
int ch = getchar(); x = 0;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
struct comp {
double x, y;
inline comp(double _x = 0, double _y = 0): x(_x), y(_y){}
inline comp operator + (const comp &o) const {return comp(x + o.x, y + o.y);}
inline comp operator * (const comp &o) const {return comp(x * o.x - y * o.y, x * o.y + y * o.x);}
inline comp operator - (const comp &o) const {return comp(x - o.x, y - o.y);}
inline comp operator ~ () const {return comp(x, -y);}
} w[2][N], A[N];
int rev[N], lim;
inline void calrev(int len){
int L = -1; lim = 1;
while(lim <= len){lim <<= 1; ++ L;}
for(Rint i = 0;i < lim;++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L);
for(Rint mid = 1;mid < lim;mid <<= 1)
for(Rint i = 0;i < mid;++ i) w[1][mid + i] = ~(w[0][mid + i] = comp(cos(PI / mid * i), sin(PI / mid * i)));
}
void FFT(comp *A, int op){
for(Rint i = 0;i < lim;++ i)
if(i < rev[i]) swap(A[i], A[rev[i]]);
for(Rint mid = 1;mid < lim;mid <<= 1)
for(Rint j = 0;j < lim;j += mid << 1)
for(Rint k = 0;k < mid;++ k){
comp x = A[j + k], y = A[j + k + mid] * w[op][mid + k];
A[j + k] = x + y; A[j + k + mid] = x - y;
}
if(op) for(Rint i = 0;i < lim;++ i) A[i].x /= lim;
}
int main(){
read(n); read(m); now = m; calrev(m << 1);
for(Rint i = 1, x;i <= n;++ i) read(x), ++ a[x];
for(Rint t = 1;t <= B;++ t){
memset(A, 0, sizeof A);
for(Rint i = 1;i <= m;++ i) A[i].x = a[i] >= t;
FFT(A, 0); for(Rint i = 0;i < lim;++ i) A[i] = A[i] * A[i]; FFT(A, 1);
for(Rint i = 1;i <= (m << 1);++ i) f[i] += A[i].x + .5;
}
for(Rint i = 1;i <= m;++ i){
a[i] -= B;
if(a[i] > 0) b[++ tot] = i;
}
for(Rint i = 1;i <= tot;++ i)
for(Rint j = 1;j <= tot;++ j) f[b[i] + b[j]] += min(a[b[i]], a[b[j]]);
for(Rint i = 1;i <= (m << 1);++ i) if(f[i] > f[ans]) ans = i;
printf("%d %d
", f[ans] >> 1, ans);
}