CDQ分治——学习笔记

CDQ分治

  • cdq分治的原理基于以下事实:

对于每个“查询”操作,其结果 a n s [ i ] = [ 1 , i − 1 ] ans[i] = [1,i-1] ans[i]=[1,i1]中所有修改对其造成影响的叠加(这里的“叠加”需要能够比较方便的维护,例如 s u m / m i n / m a x sum/min/max sum/min/max等)
定义 s o l v e ( l , r ) solve(l,r) solve(l,r)为:对于第 k k k个操作, k ∈ [ l , r ] k∈[l,r] k[l,r],若其为查询操作,则计算 [ l , k − 1 ] [l,k-1] [l,k1]中的修改对ans[i]造成的影响。设 m i d = ( l + r ) / 2 , s o l v e ( l , r ) mid = (l+r)/2,solve(l,r) mid=(l+r)/2solve(l,r)的执行步骤如下:

  1. 递归计算 s o l v e ( l , m i d ) solve(l,mid) solve(l,mid);
  2. 计算 [ l , m i d ] [l,mid] [l,mid]中所有的修改对 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]中所有查询所造成的影响;
  3. 递归计算 s o l v e ( m i d + 1 , r ) solve(mid+1,r) solve(mid+1,r);

应用

一.

  • 题目链接:https://www.luogu.com.cn/problem/P3372
const int N = 1e5 + 5;
int n , m, cnt;
ll val[N], ans[N], d[N];
struct node {
    int op, l, r, id, aid;
    ll k;
}qes[N << 1];
void solve (int l ,int r)
{
    if (l == r) return;
    int mid = (l + r) >> 1;
    solve (l, mid);
    for (int i = 1 ; i <= n ; i ++) d[i] = 0;
    for (int i = l ; i <= r ; i ++)
    {
        if (qes[i].op == 1 && qes[i].id <= mid)
        {
            d[qes[i].l] += qes[i].k;
            d[qes[i].r + 1] -= qes[i].k;
        }
    }
    for (int i = 1 ; i <= n ; i ++)
        d[i] += d[i-1];
    for (int i = 1 ; i <= n ; i ++)
        d[i] += d[i-1];
    for (int i = l ; i <= r ; i ++)
    {
        if (qes[i].op == 2 && qes[i].id > mid)
            ans[qes[i].id] += d[qes[i].r] - d[qes[i].l-1];
    }
    solve (mid + 1 , r);
}
int main()
{
    CLOSE;
    cin >> n >> m;
    for (int i = 1 ; i <= n ; i ++) {
        cin >> val[i];
        qes[i].op = 1, qes[i].l = i , qes[i].r = i , qes[i].id = i;
        qes[i].k = val[i];
    }
    for (int i = n + 1 ; i <= n + m ; i ++)
    {
        int op;
        cin >> op;
        qes[i].op = op;
        qes[i].id = i;
        if (op == 1)
            cin >> qes[i].l >> qes[i].r >> qes[i].k;
        else
            cin >> qes[i].l >> qes[i].r;
    }
    solve (1, n + m);
    for (int i = 1 ; i <= n + m ; i ++)
        if (qes[i].op == 2)
            cout << ans[i] << endl;

}

二.

  • 题目链接:https://www.luogu.com.cn/problem/P1908
  • 解题思路:将逆序对问题转换为修改和查询操作之后然后进行CDQ分治
const int N = 5e5 + 5;
int n , m, cnt = 0;
struct node {
    int op , val, id;
}q[N << 1], q1[N << 1];
int ans[N];
//op == 1 查询
bool cmp (node a, node b)
{
    if (a.val == b.val)
        return a.op < b.op;
    return a.val > b.val;
}
void solve (int l ,int r)
{
    if (l == r) return ;
    int mid = (l + r) >> 1;
    solve (l, mid);
    int tot = 0;
    sort (q + l , q + r + 1, cmp);
    for (int i = l ; i <= r ; i ++)
    {
        if (q[i].op == 2 && q[i].id <= mid) tot ++;
        if (q[i].op == 1 && q[i].id > mid) ans[q[i].id] += tot;
    }
    for (int i = l ; i <= r ; i ++)
        q[i] = q1[i];
    solve (mid + 1, r);
}
int main()
{
    CLOSE;
    cin >> n;
    for (int i = 1 ; i <= n ; i ++)
    {
        int x;
        cin >> x;
        q[++ cnt].id = cnt;
        q[cnt].val = x;
        q[cnt].op = 1;

        q[++ cnt].id = cnt;
        q[cnt].val = x;
        q[cnt].op = 2;
    }
    memcpy (q1, q, sizeof (q));
    solve (1, cnt);
    ll ANS = 0;
    for (int i = 1 ; i <= cnt ; i ++)
        ANS += ans[i];
    cout << ANS << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值