BZOJ 3295 【Cqoi2011】 动态逆序对

Description

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

Input

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

Output

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

HINT

N<=100000 M<=50000

  这道题做法很多……但是我来做这道题只是为了练CDQ分治的……

  首先,我们可以考虑当删除一个数之后逆序对数减少了多少。不难发现,减少的逆序对数就是 这个数 前面比它小的数的个数 加上 后面比它大的数的个数。

  那么,如果我们强行把最后数列中剩下的数也删掉,那么我们就得到了n个操作,用(xi,yi,zi)表示操作i是在时刻z把y位置上值为x的数给删掉。

  于是,对于一个操作i,这个操作减少的逆序对数为 xj<xi,yj<yi,zj>zi 以及 xj>xi,yj>yi,zj>zi 的j的个数。

  其实这就是一个三维偏序。对于两个式子分别在CDQ分治的时候扫一遍即可。 大概的思路就是排序一维,分治时归并一维,剩下一维再用树状数组来维护。

  下面贴代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
 7 #define maxn 100010
 8
 9 using namespace std;
10 typedef long long llg;
11
12 struct data{
13     int x,y,b;
14     bool operator < (const data &h)const{return x>h.x;}
15 }s[maxn],ss[maxn];
16 int c[maxn],n,m,a[maxn],ans[maxn];
17 bool w[maxn]; llg ana;
18
19 int getint(){
20     int w=0;bool q=0;
21     char c=getchar();
22     while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar();
23     if(c==‘-‘) c=getchar(),q=1;
24     while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar();
25     return q?-w:w;
26 }
27
28 void add(int x,int y){while(x<=n) c[x]+=y,x+=x&(-x);}
29 int sum(int x){
30     int t=0;
31     while(x) t+=c[x],x-=x&(-x);
32     return t;
33 }
34
35 void solve(int l,int r){
36     if(l>=r) return;
37     int mid=l+r>>1,now=l,kk=l-1,k1=l,k2=mid+1;
38     solve(l,mid); solve(mid+1,r);
39     for(int i=mid+1;i<=r;i++){
40         while(s[now].y<s[i].y && now<=mid) add(s[now].b,1),now++;
41         ans[s[i].b]+=sum(n)-sum(s[i].b);
42     }
43     for(int i=l;i<now;i++) add(s[i].b,-1);
44     now=r;
45     for(int i=mid;i>=l;i--){
46         while(s[now].y>s[i].y && now>mid) add(s[now].b,1),now--;
47         ans[s[i].b]+=sum(n)-sum(s[i].b);
48     }
49     for(int i=now+1;i<=r;i++) add(s[i].b,-1);
50     while(k1<=mid && k2<=r)
51         if(s[k1].y<s[k2].y) ss[++kk]=s[k1++];
52         else ss[++kk]=s[k2++];
53     while(k1<=mid) ss[++kk]=s[k1++];
54     while(k2<=r) ss[++kk]=s[k2++];
55     for(int i=l;i<=r;i++) s[i]=ss[i];
56 }
57
58 int main(){
59     File("a");
60     n=getint(); m=getint();
61     for(int i=1;i<=n;i++) a[getint()]=i;
62     for(int i=1;i<=m;i++){
63         s[i].x=getint(); s[i].b=i;
64         s[i].y=a[s[i].x]; w[s[i].x]=1;
65     }
66     for(int i=1,t=m;i<=n;i++)
67         if(!w[i]){
68             s[++t].x=i; s[t].b=t;
69             s[t].y=a[s[t].x];
70         }
71     sort(s+1,s+n+1); solve(1,n);
72     for(int i=1;i<=n;i++) ana+=ans[i];
73     for(int i=1;i<=m;i++){
74         printf("%lld\n",ana);
75         ana-=ans[i];
76     }
77 }
时间: 2024-10-12 16:34:46

BZOJ 3295 【Cqoi2011】 动态逆序对的相关文章

主席树初探 &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,即初始元素的个数和删除的元

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

Description 对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数 Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数. 以下n行每行包含一个1到n之间的正整数,即初始排列. 以下m行每行一个正整数,依次为每次删除的元素. N<=100000 M<=50000 Output 输出包含m行,依次为删除每个元素之前,逆序对的个数. Sample In

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

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

bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

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

bzoj 3295: [Cqoi2011]动态逆序对

2016-06-22 这个题本想昨天晚上做来,但昨晚狂风大作,暴雨倾盆(听说我们学校最落后的一堵墙都被吹到了),停电了,我只能无聊的瞭望了教学楼一晚上...... 这个题把删除看成插入的话,插入一个点 新增逆序对就是比他早插入的,位置靠前,数比他大或 位置靠后,数比他小.那这就是个三维偏序集,可以用CDQ搞搞了. 这个题也能用树套树,树状数组归并排序做(以后有时间要写一写,恐怕是没可能了%>_<%). 1 #include<cstdio> 2 #include<iostrea

COGS 1715 &amp; bzoj 3295 [CQOI2011]动态逆序对 题解

(又是一道树套树……自己真是玩疯了……) (题意略) 从网上也看过题解,好像解法很多……比如CDQ+树状数组,树状数组套主席树,树状数组套平衡树……我用的是树状数组套splay. (我会说是因为我不会写CDQ和树状数组套主席树么= =) (不得不吐槽,为啥splay这么快= =) 也没啥可说的,我写的是在线算法,只要在删除一个元素之前统计它前面比它大的数和后面比它小的数的个数(区间求和用树状数组,统计比它小/大的数的个数用平衡树写),把答案减掉对应数值即可. 鉴于这题卡常,我就加了快读和各种in

BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接 方法:树状数组套线段树 解析: 这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队.这道题比那道更要简单. 然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy.引用理论部分. 删除某个数,只要统计它之前还存在的比它大的

【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,即初始元素的个数

[CQOI2011]动态逆序对

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

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