bzoj3295动态逆序对

Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 5362  Solved: 1814
[Submit][Status][Discuss]

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。

Output

输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2

2
1

样例解释
(1,5,3,4,2)?(1,3,4,2)?(3,4,2)?(3,2)?(3)。

这道题就是求每个值插进去后会形成多少个逆序对,前提是另外一个数的出现时间比这个数小。这样可以保证逆序对数不会重复,也不会漏算。

CDQ分治吧,动态树LCT我不会。

抽离出模型,就是求(i,j)|(ti<tj,xi<xj,vali>valj)和(ti<tj,xi>xj,vali<valj)个数

外层按t值排序,然后将区间(l,r)分成(l,mid)和(mid+1,r);(l,mid)中的t肯定比(mid+1,r)中的小。

然后用个辅助数组中按x排序,考虑对右半部分的贡献,(这是CDQ分治的精髓)利用树状数组的方法求出答案。

其实写的慢出翔,不用辅助数组或者按归并排序对t排序都会快的多

T(n)=2T(n/2)+nlogn的时间复杂度是nlognlogn,用归并可以去掉1个logn吧

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 100011
struct note{
    int x,y,t;
    int flag;
}a[maxn],b[maxn];
int n,m,c[maxn],pos[maxn],ans[maxn];
#define lowbit(x) x&-x
inline bool cmpx(note q,note qq)
{
    if(q.x==qq.x){return q.y<qq.y;}
    return q.x<qq.x;
}
inline bool cmpt(note q,note qq){ return q.t<qq.t; }
void add(int u,int v){for(int j=u;j<=n;c[j]+=v,j+=lowbit(j));}
int query(int u){int sum=0;for(int j=u;j;sum+=c[j],j-=lowbit(j));return sum;}

void CDQ(int l,int r)
{
    if(l>=r)return;
    int mid=(l+r)>>1,size=r-l+1,cnt=0;
    for(int i=l;i<=mid;i++)b[++cnt]=a[i],b[cnt].flag=0;for(int i=mid+1;i<=r;i++) b[++cnt]=a[i],b[cnt].flag=1;
    sort(b+1,b+1+cnt,cmpx);
    for(int i=1;i<=size;i++)
    if(!b[i].flag) add(b[i].y,1);
    else ans[b[i].t]+=query(n)-query(b[i].y);
    for(int i=1;i<=size;i++)
        if(!b[i].flag)add(b[i].y,-1);
     for(int i=size;i>=1;i--)
         if(!b[i].flag)add(b[i].y,1);else ans[b[i].t]+=query(b[i].y);
    for(int i=size;i>=1;i--)
        if(!b[i].flag)add(b[i].y,-1);
     CDQ(l,mid);if(mid<r) CDQ(mid+1,r);
}
void debug()
{
    for(int i=1;i<=n;i++)
    cout<<a[i].x<<‘ ‘<<a[i].y<<‘ ‘<<a[i].t<<endl;
}
int main()
{
    scanf("%d %d",&n,&m);
    int Timerec=n;
    for(int i=1;i<=n;i++){int x;scanf("%d",&x);a[i].x=i,a[i].y=x;pos[x]=i;}
    for(int i=1;i<=m;i++){int x;scanf("%d",&x);a[pos[x]].t=Timerec--;}
    //debug();
    for(int i=1;i<=n;i++)if(!a[i].t){a[i].t=Timerec--;}
    sort(a+1,a+1+n,cmpt);CDQ(1,n);
    long long Ans=0;
    for(int i=1;i<=n;i++) Ans+=ans[i];
    for(int i=n;i>n-m;i--)
    {
        printf("%lld\n",Ans);
        Ans-=ans[i];
    }

}

http://www.hekai.site/wordpress/2017/08/26/cdq%E5%88%86%E6%B2%BB%E8%AF%A6%E8%A7%A3%EF%BC%88bzoj3262-%E9%99%8C%E4%B8%8A%E8%8A%B1%E5%BC%80%EF%BC%89/

HKdalao的题解,题目不一样,但思路差不多吧

时间: 2024-10-08 21:35:38

bzoj3295动态逆序对的相关文章

BZOJ3295 动态逆序对 树套树, 树状数组套线段树(主席树)

Orz黄学长,蒟蒻在黄学长的带领下,通过阅读黄学长的代码!终于会了这道题! 首先我想先说一下这道题的思路(准确来说是黄学长的). 很明显,树状数组应该不用讲吧!关键是内存怎么开,维护一些什么样的数据? 其实我们通过观察,很快可以发现,你维护被删的数比维护所有的数轻松多了(不管是空间上,还是时间上).所以我们就可以从这方面想!(其实我一开始的思路,因为这道题我已经看过很久了,一直想写,毕竟是白书里面的一道例题嘛!一开始,蒟蒻的我是打算这样的用树状数组套权值线段树,并且是维护所有的数,我发现空间不够

【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行,依次为删除每个元素之前,逆序对的

bzoj3295【CQOI2011】动态逆序对

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

[CQOI2011]动态逆序对

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

主席树初探 &amp; bzoj 3295: [Cqoi2011] 动态逆序对 题解

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

Bestcoder7(1004)hdu4988(经典问题:树状数组套treap求解动态逆序对)

Little Pony and Boast Busters Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 83    Accepted Submission(s): 32 Problem Description "I hereby challenge you, Ponyvillians: anything you can do

【BZOJ 3295】 [Cqoi2011]动态逆序对

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

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

[Luogu1393]动态逆序对(CDQ分治) 题面 题目描述 对于给定的一段正整数序列,我们定义它的逆序对的个数为序列中ai>aj且i < j的有序对(i,j)的个数.你需要计算出一个序列的逆序对组数及其删去其中的某个数的逆序对组数. 输入输出格式 输入格式: 第一行,两个数n,m,表示序列中有n个数,要删去m个数 第二行n个数,表示给定的序列. 第三行m个数,第i个数di表示要删去原序列中的第di个数. 输出格式: 一行m+1个数.第一个数表示给定序列的逆序对组数,第i+1个数表示删去第d

【算法】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