P3157 [CQOI2011]动态逆序对

P3157 [CQOI2011]动态逆序对

https://www.luogu.org/problemnew/show/P3157

题目描述

对于序列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
4
2

输出样例#1:

5
2
2
1

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

说明

N<=100000 M<=50000

树状数组套主席树

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<set>
  7 #include<map>
  8 #include<vector>
  9 #include<queue>
 10 #define N 100005
 11 using namespace std;
 12
 13 struct sair{
 14     int l,r,sum;
 15 }tree[N*150];
 16 int n;
 17 int root[N],a[N],b[N],c[N],cnt;
 18 void Add(int x){
 19     while(x<=n){
 20         c[x]++;
 21         x+= x&(-x);
 22     }
 23 }
 24
 25 int getsum(int x){
 26     int sum=0;
 27     while(x){
 28         sum+=c[x];
 29         x-= x&(-x);
 30     }
 31     return sum;
 32 }
 33
 34 void add(int cur,int l,int r,int p,int v){
 35     tree[cur].sum+=v;
 36     if(l==r){
 37         return;
 38     }
 39     int mid=(l+r)/2;
 40     if(p<=mid){
 41         if(!tree[cur].l){
 42             tree[cur].l=++cnt;
 43         }
 44         add(tree[cur].l,l,mid,p,v);
 45     }
 46     else{
 47         if(!tree[cur].r){
 48             tree[cur].r=++cnt;
 49         }
 50         add(tree[cur].r,mid+1,r,p,v);
 51     }
 52 }
 53
 54 int query(int L,int R,int cur,int l,int r){
 55     if(!cur){
 56         return 0;
 57     }
 58     if(L<=l&&R>=r){
 59         return tree[cur].sum;
 60     }
 61     int mid=(l+r)/2;
 62     int ans=0;
 63     if(L<=mid) ans+=query(L,R,tree[cur].l,l,mid);
 64     if(R>mid) ans+=query(L,R,tree[cur].r,mid+1,r);
 65     return ans;
 66 }
 67
 68 int main(){
 69     int m,v,p,j;
 70     scanf("%d %d",&n,&m);
 71     long long ans=0;
 72     for(int i=1;i<=n;i++){
 73         scanf("%d",&a[i]);
 74         b[a[i]]=i;
 75         Add(a[i]);
 76         ans+=getsum(n)-getsum(a[i]);
 77         for(j=i;j<=n;j+= j&(-j)){
 78             if(!root[j]){
 79                 root[j]=++cnt;
 80             }
 81             add(root[j],1,n,a[i],1);
 82         }
 83     }
 84     for(int i=1;i<=m;i++){
 85         printf("%lld\n",ans);
 86         scanf("%d",&v);
 87         p=b[v];
 88         j=p-1;
 89         while(j){
 90             ans-=query(v+1,n,root[j],1,n);
 91             j-= j&(-j);
 92         }
 93         j=n;
 94         while(j){
 95             ans-=query(1,v-1,root[j],1,n);
 96             j-= j&(-j);
 97         }
 98         j=p;
 99         while(j){
100             ans+=query(1,v-1,root[j],1,n);
101             j-= j&(-j);
102         }
103         j=p;
104         while(j<=n){
105             add(root[j],1,n,v,-1);
106             j+= j&(-j);
107         }
108     }
109 }

原文地址:https://www.cnblogs.com/Fighting-sh/p/9748857.html

时间: 2024-10-08 15:41:55

P3157 [CQOI2011]动态逆序对的相关文章

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

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

[Luogu P3157][CQOI2011]动态逆序对 (树套树)

题面 传送门:[CQOI2011]动态逆序对 Solution 一开始我看到pty巨神写这套题的时候,第一眼还以为是个SB题:这不直接开倒车线段树统计就完成了吗? 然后冷静思考了一分钟,猛然发现单纯的线段树并不能解决这个问题,好像还要在外面再套上一颗树. 这就很shit了.你问我资磁不资磁树套树,我是不资磁的,树套树是暴力数据结构,我能资磁吗? 很不幸,昨天现实狠狠地打了我一脸:时间不够开新坑的,不切题又浑身难受,找了半天题,还是把这道题拉了出来(哈,真香) 不扯淡了,这题还是很显然的. 考虑开

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

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

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

题解-P3157 [CQOI2011]动态逆序对

树状数组套线段树,用动态开点线段树.此类就这一题放在这吧. 程序写的太丑了,不要介意. #include<iostream> #include<cstdio> using namespace std; typedef long long i64; const int N=1e5+5; const int S=1e7+7; int a[N],p[N]; int q[N]; bool b[N]; int r[N]; int cnt,x[S],c[S][2]; i64 ans[N]; v

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]动态逆序对】

题目链接 Solution [CQOI2011]动态逆序对 题目大意:给定一个\(n\)个数的排列,依次删除\(m\)个元素,询问删除每个元素之前的逆序对数量 分析:对于这种依次删除元素的问题,我们的通常解法是时间倒流,顺序删除变逆序插入,那么问题就转化为了每插入一个数之后(对应删除之前)询问逆序对数量. 我们设元素\(i\)的时间戳为\(T_i\)(对于那些没有被删除的元素,\(T_i = 0\)),位置为\(P_i\),权值为\(V_i\) 那么,如果插入元素\(j\)后,元素\(i\)与元

[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,即初始元素的个数和删除的元