[BZOJ 4836] 二元运算

本文介绍了一道关于CDQ分治与FFT的应用题目,通过详细解析算法思路及实现代码,展示了如何处理两个数组中元素的组合问题,并利用快速傅立叶变换加速计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目传送门

这是一道很有意思的  裸题   。

首先我们可以考虑 a 和 b 的值域是固定在 0~50000之间的,当 ai  < bi 时,根据题意对答案 ai+bi的贡献为 ai的个数*bi的个数

所以我们可以把值得个数记下来。(下文中的Ai表示值为a中值为i的数的个数,Bi同理,Ci为值为i的答案的个数)

 

考虑使用cdq分治¿

首先 对于 Ai 与 Bj ,如果 i < j,则对答案Ci+j 有Ai*Bj的贡献,对于 i > j ,则对答案Ci-j有Ai*Bj的贡献

显然,这是cdq分治的经典题目,对于算贡献,我们可以使用 fft 进行加速

 

(然而因为手懒使用系统的复数complex结果T到怀疑人生)

/**************************************************************
    Problem: 4836
    User: MiEcoku
    Language: C++
    Result: Accepted
    Time:3988 ms
    Memory:10980 kb
****************************************************************/
 
#include <cstdio>
#include <algorithm>
#include <complex>
#include <cstring>
#include <cmath>
using namespace std;
#define fp(i, a, b) for ( register int i = (a), I = (b); i <= I; ++ i)
#define mid ( l + r >> 1)
#define mem(o) memset(o, 0, sizeof(o))
struct cp {
    double x, y;
    cp ( double x, double y) : x(x), y(y) {}
    cp () {}
};
cp operator + (const cp &a, const cp &b) { return cp ( a.x+b.x, a.y+b.y); }
cp operator - (const cp &a, const cp &b) { return cp ( a.x-b.x, a.y-b.y); }
cp operator * (const cp &a, const cp &b) { return cp ( a.x*b.x-a.y*b.y, a.x*b.y+a.y*b.x); }
typedef long long LL;
const double pi = acos(-1);
const int maxn = 200000;
int rve[maxn];
inline void fft(cp *a, int n, int f) {
    fp(i, 1, n) if( i < rve[i]) swap(a[i], a[rve[i]]);
    for ( int i = 1; i < n; i <<= 1) {
        cp wn (cos(pi/i), f*sin(pi/i));
        for ( int j = 0; j < n; j += (i << 1)) {
            cp w (1, 0);
            for ( int k = 0; k < i; ++ k, w = w * wn) {
                cp x = a[j+k], y = w * a[j+k+i];
                a[j+k] = x + y; a[j+k+i] = x - y;
            }
        }
    }
    if( f == -1) fp(i, 0, n) a[i].x /= n;
}
template <class T> inline void Max(T &x, const T &a) { 
    if( a > x) x = a; return ;
}
//template <class T> 
inline void G(int &x) {
    x = 0; char o; for ( ; !isdigit(o = getchar()); ) ;//if( o == '-') f = -1;
    for ( ; isdigit(o); o = getchar()) x = (x << 1) + (x << 3) + (o & 15);
}
int a[maxn], b[maxn];
LL C[maxn];
cp t1[maxn], t2[maxn];
inline void cdq(int l, int r) {
//  printf("%d
    if( l == r) { C[0] += 1LL * a[l] * b[l]; return ; }
    if( r - l < 200) { // 网上学的,据说可以加速很多?(雾)
        fp(i, l, mid) if( a[i] || b[i]) {
            fp(k, mid+1, r) C[i+k] += 1LL * a[i] * b[k], C[k-i] += 1LL * a[k] * b[i]; 
        } 
        cdq(l, mid); cdq(mid+1, r); return ;
    }
    int n = 1, len = 0; while ( n < r - l + 1) n <<= 1, ++ len;
    fp(i, 1, n) rve[i] = (rve[i >> 1] >> 1) | ((i&1) << len-1);
    fp(i, 0, n) t1[i] = cp(0,0), t2[i] = cp(0,0);
    fp(i, l, mid) t1[i-l].x = a[i]; fp(i, mid+1, r) t2[i-mid].x = b[i];
    fft(t1, n, 1); fft(t2, n, 1);
    fp(i, 0, n) t1[i] = t1[i] * t2[i]; fft(t1, n, -1);
    fp(i, 0, r - l) C[i+l+mid] += (LL) floor (t1[i].x + 0.5);
    fp(i, 0, n) t1[i] = cp(0,0), t2[i] = cp(0,0);
    fp(i, l, mid) t2[mid-i].x = b[i]; fp(i, mid+1, r) t1[i-mid].x = a[i];
    fft(t1, n, 1); fft(t2, n, 1);
    fp(i, 0, n) t1[i] = t1[i] * t2[i]; fft(t1, n, -1);
    fp(i, 1, r - l) C[i] += (LL) floor (t1[i].x + 0.5);
    cdq(l, mid); cdq(mid+1, r);
}
int T, n, m, q;
int main() {
#ifndef ONLINE_JUDGE
    freopen("1.in", "r", stdin);
    freopen("a.out", "w", stdout);
#endif
    G(T);
    while ( T -- ) {
        mem(a); mem(b); mem(C);
        G(n); G(m); G(q);
        int L = 0, R = 0, x;
        fp(i, 1, n) G(x), ++ a[x], Max(R, x);
        fp(i, 1, m) G(x), ++ b[x], Max(R, x);
        cdq(L, R); 
        while ( q --) G(x), printf("%lld\n", C[x]);
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/miecoku/p/9672495.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值