Luogu6300 悔改 [FFT,阈值法]

题目描述:小 D 有一些同样长的木棍,然后每个都切成长度分别不超过 \(m\) 的两段。现在想拼回去,但是小 D 遗失了一部分木棍,而且还忘了它们的长度和个数,所以他想拼接出尽可能多的相同长度的木棍。给出每段小木棍的长度,求尽可能多的木棍个数和此时的木棍最小长度。

数据范围:\(2\le n,m\le 10^5,1\le a_i\le m\)


开桶 \(\{b_m\}\)

\[\begin{aligned} Ans_x&=\frac12\sum_{i+j=x}\min(b_i,b_j) \\ &=\sum_k\sum_{i+j=x}[b_i\ge k][b_j\ge k] \end{aligned} \]

使用 FFT,成功得到了一个 \(O(nm\log m)\) 的比暴力还lj的做法。

但是你发现 \(\sum b_i=n\le 10^5\),所以你可以"阈值法"(?),定一个数 \(t\),当 \(k\le t\) 时用 FFT 计算,\(b_i>t\) 的数不超过 \(\frac nt\) 个,所以时间复杂度是 \(O(tm\log 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\n", f[ans] >> 1, ans);
}

猜你喜欢

转载自www.cnblogs.com/AThousandMoons/p/12675694.html
今日推荐