[科技] 分块 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
还没搞明白复杂度为啥能变好,但是事实就是如此,这就是分块带给我的自信。