CDQ分治——学习笔记

CDQ分治

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

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

  1. 递归计算solve(l,mid)solve(l,mid)solve(l,mid);
  2. 计算[l,mid][l,mid][l,mid]中所有的修改对[mid+1,r][mid+1,r][mid+1,r]中所有查询所造成的影响;
  3. 递归计算solve(mid+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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值