[科技] 分块 FFT

其实也不是啥科技。

就是一些卷积形式的东西,直接 FFT 复杂度非常大。

但是你分块以后再 FFT / NTT 就很牛逼。

CodeChef COUNTARI

按照 \(i, j, k\) 在哪个块中,分类讨论,除了 \(i, j, k\) 分别属于不同块,都可以暴力。剩余部分直接卷积。。。

点击查看代码
// 僕らタイムフライヤー \
   時を駆け上がるクライマー \
   時のかくれんぼ \
   はぐれっこはもういやなんだ
#include <bits/stdc++.h>
#include <assert.h>
using namespace std;
typedef long long ll;
typedef double db;
#define ep emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define fout freopen("out.out","w",stdout);
#define fin freopen("in.in","r",stdin);
#define dd(x) cerr << #x" = " << x << endl;
inline int read() {
	int x=0, v=1,ch=getchar();
	while('0'>ch||ch>'9') {
		if(ch=='-') v=0;
		ch=getchar();
	}while('0'<=ch&&ch<='9') {
		x=(x*10)+(ch^'0');
		ch=getchar();
	}return v?x:-x;
}
const int MAX_V = 60005, MAX = 1e5 + 5;
int n, num[MAX_V], a[MAX];
ll ans;
void BF(int l, int r) {
	for(int i = 1; i < l; ++ i) ++ num[a[i]];
	for(int j = l; j <= r; ++ j) {
		for(int k = j + 1; k <= r; ++ k) {
			const int now = a[j] * 2 - a[k];
			if(now >= 0) ans += num[now];
		}
	}
	for(int i = 1; i < l; ++ i) -- num[a[i]];
	
	for(int i = r + 1; i <= n; ++ i) ++ num[a[i]];
	for(int j = l; j <= r; ++ j) {
		for(int i = l; i < j; ++ i) {
			const int now = a[j] * 2 - a[i];
			if(now >= 0) ans += num[now];
		}
	}
	for(int i = r + 1; i <= n; ++ i) -- num[a[i]];
	
	++ num[a[l]];
	for(int j = l + 1; j <= r; ++ j) {
		for(int k = j + 1; k <= r; ++ k) {
			const int now = a[j] * 2 - a[k]; 
			if(now >= 0) ans += num[now];
		}
		++ num[a[j]];
	}
	for(int j = l; j <= r; ++ j) -- num[a[j]];
}

namespace poly {
	const int P = 998244353, G = 3, iG = 332748118;
	const int N = ((1 << 17) + 10);
	#define clr(f, n) memset(f, 0, sizeof(int) * n)
	#define cpy(f, g, n) memcpy(f, g, sizeof(int) * n)
	int tr[N], now = 0;
	int qpow(int x, int p) {
		int ret = 1;
		for(; p; p >>= 1, x = 1ll * x * x % P) if(p & 1) ret = 1ll * ret * x % P;
		return ret; 
	}
	void tpre(int n) {
		if(n == now) return ; now = n;
		for(int i = 0; i < n; ++ i) tr[i] = (tr[i >> 1] >> 1) | ((i & 1) ? (n >> 1) : 0);
	}
	ll inc(ll a, ll b) { a += b; return (a >= P) ? a - P : a; }
	void NTT(ll *f, int n, int op) {
		tpre(n);
		for(int i = 0; i < n; ++ i) if(i < tr[i]) swap(f[i], f[tr[i]]);
		static ll w[N] = {1}; 
		for(int l = 1; l < n; l <<= 1) {
			int wG = qpow(op ? G : iG, (P - 1) / (l << 1));
			for(int i = 1; i < l; ++ i) w[i] = w[i - 1] * wG % P;
			for(int i = 0; i < n; i += (l << 1)) {
				for(int j = 0; j < l; ++ j) {
					ll a = f[i | j], b = f[i | j | l] * w[j] % P;
					f[i + j] = inc(a, b);
					f[i + j + l] = inc(a, P - b);
				}
			}
		}
		if(op == 0) {
			int invn = qpow(n, P - 2);
			for(int i = 0; i < n; ++ i) f[i] = f[i] * invn % P;
		}
	}
	void px(ll *f, ll *g, int n) {
		for(int i = 0; i < n; ++ i) f[i] = f[i] * g[i] % P; 
	}
	void times(ll *f, ll *g, int len, int lim) {
		int n = 1; while(n < len + len) n <<= 1;
		NTT(f, n, 1), NTT(g, n, 1);
		px(f, g, n), NTT(f, n, 0);
	}
}
using namespace poly;

ll A[N], B[N];
signed main() {
	fin; 
 	n = read();
 	for(int i = 1; i <= n; ++ i) a[i] = read();
 	int BL = 2000;
 	int LIM = (n - 1) / BL + 1, l = 0, r = 0;
 	for(int i = 1; i <= LIM; ++ i) {
 		l = r + 1, r += BL; if(r > n) r = n;
		BF(l, r);	
		for(int j = 1; j <= l - 1; ++ j) ++ A[a[j]];
		for(int j = r + 1; j <= n; ++ j) ++ B[a[j]];
		times(A, B, 60000, 60001); 
		for(int j = l; j <= r; ++ j) ans += A[a[j] * 2];
		for(int j = 0; j < N; ++ j) A[j] = B[j] = 0;
	}
	cout << ans; 
	return 0;
}

这一场的 C

[GDKOI Day1 T2]

题解随意(

Summary

还没搞明白复杂度为啥能变好,但是事实就是如此,这就是分块带给我的自信。

posted @ 2023-03-10 23:21  Lates  阅读(138)  评论(0编辑  收藏  举报