【Luogu1393】动态逆序对(CDQ分治)

【Luogu1393】动态逆序对(CDQ分治)

题面

题目描述

对于给定的一段正整数序列,我们定义它的逆序对的个数为序列中ai>aj且i < j的有序对(i,j)的个数。你需要计算出一个序列的逆序对组数及其删去其中的某个数的逆序对组数。

输入输出格式

输入格式:

第一行,两个数n,m,表示序列中有n个数,要删去m个数

第二行n个数,表示给定的序列。

第三行m个数,第i个数di表示要删去原序列中的第di个数。

输出格式:

一行m+1个数。第一个数表示给定序列的逆序对组数,第i+1个数表示删去第di个数后序列的逆序对组数(删去的数不再恢复)

输入输出样例

输入样例#1:

6 3
5 4 2 6 3 1
2 1 4

输出样例#1:

11 7 4 2

说明

对于20%的数据,n≤2500

对于另30%的数据,m=0

对于100%的数据,n≤40000,m≤n/2,且保证第二行n个数互不相同,第三行m个数互不相同

题解

之前不是说过要写一遍CDQ分治吗??
在这里说的
可是,当你把上面的代码兴高采烈的Copy到洛谷上之后
你就会直接WA了
因为,题目还是有点不同的(仔细读题)
区别一:这题不是排列,要离散化
区别二:这题删掉的不是数字,而是位置

好了回归正题,讲讲CDQ分治怎么写
首先,给所有删掉的数编个号,就按照删去的顺序来吧
没有删掉的数就编个INF吧

那么,删掉这个数之后,减少的逆序对对数是:
对于\(j\in[1,j]\)
\(t[i]<t[j]\),其中t是删除的编号
并且
\(i<j,a[i]>a[j]\)
或者
\(i>j,a[i]<a[j]\)
所以,删除的编号直接sort搞完
剩下的两维CDQ分治

于是,发现这个玩意是一个三维偏序
所以之前写过的树状数组套平衡树当然也可以做啦
但是,CDQ分治还是要会嗷。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 50000
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,m,S[MAX],a[MAX],b[MAX],c[MAX],d[MAX];
long long ans;
int lowbit(int x){return x&(-x);}
void Add(int x,int w){while(x<=n)c[x]+=w,x+=lowbit(x);}
int getsum(int x){int ret=0;while(x)ret+=c[x],x-=lowbit(x);return ret;}
struct Node
{
    int t,p,a;
    int s;
}t[MAX];
bool operator<(Node a,Node b){return a.t<b.t;}
bool cmp(Node a,Node b){return a.p<b.p;}
void CDQ(int l,int r)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ(l,mid);CDQ(mid+1,r);
    sort(&t[l],&t[mid+1],cmp);
    sort(&t[mid+1],&t[r+1],cmp);
    int j=mid;
    for(int i=l;i<=mid;++i)
    {
        while(j<r&&t[j+1].p<t[i].p)++j,Add(t[j].a,1);
        t[i].s+=getsum(n)-getsum(t[i].a);
    }
    for(int i=mid+1;i<=j;++i)Add(t[i].a,-1);
    j=r+1;
    for(int i=mid;i>=l;--i)
    {
        while(j>mid+1&&t[j-1].p>t[i].p)--j,Add(t[j].a,1);
        t[i].s+=getsum(t[i].a-1);
    }
    for(int i=r;i>=j;--i)Add(t[i].a,-1);
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)S[i]=a[i]=read();
    sort(&S[1],&S[n+1]);
    for(int i=1;i<=n;++i)b[a[i]=lower_bound(&S[1],&S[n+1],a[i])-S]=i;
    for(int i=n;i;i--)ans+=getsum(a[i]),Add(a[i],1);
    for(int i=1;i<=n;++i)t[i].t=n+1,t[i].p=i,t[i].a=a[i];
    for(int i=1;i<=m;++i)
    {
        d[i]=read();
        t[d[i]].t=i;
    }
    sort(&t[1],&t[n+1]);
    memset(c,0,sizeof(c));
    CDQ(1,n);
    for(int i=1;i<=n;++i)c[t[i].p]=t[i].s;
    printf("%lld ",ans);
    for(int i=1;i<=m;++i)
        printf("%lld ",ans=ans-c[d[i]]);
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/8127824.html

时间: 2024-08-01 03:24:43

【Luogu1393】动态逆序对(CDQ分治)的相关文章

P3157 动态逆序对 CDQ分治

动态逆序对 CDQ分治 传送门:https://www.luogu.org/problemnew/show/P3157 题意: 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 题解: 这个题是告诉你如何将一个问题转换为三维偏序问题 首先,求解逆序对,那么a.val>b.val,删除一个元素的时间是t,a.t<b.t,这个元素对应的原序列中的位置为

【BZOJ3295】[Cqoi2011]动态逆序对 cdq分治

[BZOJ3295][Cqoi2011]动态逆序对 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的

BZOJ 3295: [Cqoi2011]动态逆序对 cdq分治

https://www.lydsy.com/JudgeOnline/problem.php?id=3295 这个妹妹我曾见过的~~~ 之前应该在校内oj写了,似乎还写过题解?发现没写博客就重新水一遍代码水一篇博客好了. 把找逆序对的过程想成一个一个往里塞数字然后找每个数字可以组成的逆序对,把最后要去掉的几个也想成往里塞,所以加一个时间维度用cdq求三维偏序. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorith

P3157 [CQOI2011]动态逆序对(CDQ分治)

题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 输入格式 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. 输出格式 输出包含m行,依次为删除每个元素之前,逆序对的个数. 输入输出样例 输入 #1复制 5 4 1 5 3 4 2 5 1

LUOGU P3157 [CQOI2011]动态逆序对(CDQ 分治)

传送门 解题思路 cdq分治,将位置看做一维,修改时间看做一维,权值看做一维,然后就转化成了三维偏序,用排序+cdq+树状数组.注意算删除贡献时要做两次cdq,分别算对前面和后面的贡献. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 100005; const int MAXM =

P3157 [CQOI2011]动态逆序对 (CDQ解决三维偏序问题)

P3157 [CQOI2011]动态逆序对 题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 输入格式 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. 输出格式 输出包含m行,依次为删除每个元素之前,逆序对的个数. 输入输出样例 输入

bzoj 3295 动态逆序对 CDQ分支

容易看出ans[i]=ans[i-1]-q[i],q[i]为删去第i个数减少的逆序对. 先用树状数组算出最开始的逆序对,预处理出每个数前边比它大的和后边比它小的,就求出了q[i]的初始值. 设b[i]是第i个删除的数,pos[i]为i在数列里的位置. 对q[i]产生影响的是   1. j<i,pos[b[j]]<pos[b[i]],b[j]>b[i].  2.j<i,pos[b[j]]>pos[b[i]],b[j]<b[i]. 因为是三维的,所以我们套一层cdq. 1

【算法】CDQ分治 -- 三维偏序 &amp; 动态逆序对

初次接触CDQ分治,感觉真的挺厉害的. 整体思路即分而治之,再用之前处理出来的答案统计之后的答案. 大概流程是: 对于区间 l ~ r : 1.处理 l ~mid, mid + 1 ~ r 的答案 2.分别排序规整 3.计算 l ~ mid 中每一个数对 mid + 1 ~ r 中的答案的贡献, 累加 4.得到区间l ~ r的答案 CDQ分治我一共也才做了两道题目, 就一起整理在这里了.大体都差不多,CDQ+树状数组分别维护两个维度. 1.三维偏序 #include <bits/stdc++.h

bzoj3295动态逆序对

Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 5362  Solved: 1814[Submit][Status][Discuss] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数