[BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树)

题面

给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数

分析

每次都对整个序列求逆序对显然不行,考虑每次删除对逆序对个数的影响

假如删除的数为x,x在序列中的位置为pos[x],那么包含x的逆序对个数为位置在[1,pos[x]-1]中大于x的数+位置在[pos[x]+1,n]中小于x的数,每次删除只要减去这些就可以了

那么这个问题其实就转化成查询位置在[L,R]内,且值域在[l,r]内的数的个数。本质上和带修区间第k大BZOJ 1901 Dynamic Rankings是一样的,外层用树状数组维护位置,内层不用可持久化,直接用权值线段树即可

那么我们像树状数组那样维护n棵权值线段树,不同的是每棵权值线段树里保存的是,a[i-lowbit(i)+1]~a[i]有多少个值落在区间[l,r]内

对于查询[ql,qr]中有多少个值时的做差

我们要像树状数组求和那样,把root[i],root[i-lowbit(i)],....共O(logn)棵权值线段树的值加起来,才能得到a[1]a[i]有多少个数落在1qr里面,1~(ql-1)同理,维护两个临时数组x,y存储这O(logn)棵权值线段树的根即可

对于单点修改,我们像树状数组修改那样,往O(logn)棵权值线段树中插入即可

时间复杂度\(O(m \log ^2 n)\),常数略大

注意答案一定要开long long!!!!

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100000
#define maxm 100000
#define maxnlogn 50000000
#define maxlogn 20
using namespace std;
inline void qread(int &x) {
x=0;
int sign=1;
char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
x=x*sign;
}
inline void qprint(long long x) {
if(x<0) {
putchar('-');
qprint(-x);
} else if(x==0) {
putchar('0');
return;
} else {
if(x>=10) qprint(x/10);
putchar('0'+x%10);
}
} int n,m;
int a[maxn+5];
struct segment_tree_val{
#define lson(x) (tree[x].ls)
#define rson(x) (tree[x].rs)
struct node{
int ls;
int rs;
int v;
}tree[maxnlogn+5];
int ptr;
void push_up(int x){
tree[x].v=tree[lson(x)].v+tree[rson(x)].v;
}
void update(int &x,int upos,int uval,int l,int r){
if(!x) x=++ptr;
if(l==r){
tree[x].v+=uval;
return;
}
int mid=(l+r)>>1;
if(upos<=mid) update(tree[x].ls,upos,uval,l,mid);
else update(tree[x].rs,upos,uval,mid+1,r);
push_up(x);
}
int totx,toty;
int x[maxn+5],y[maxn+5];
int query_less(int k,int l,int r){//查询<k的数的个数
int cnt=0;
while(l!=r){
int mid=(l+r)>>1;
if(k<=mid){
for(int i=1;i<=totx;i++) x[i]=lson(x[i]);
for(int i=1;i<=toty;i++) y[i]=lson(y[i]);
r=mid;
}else{
for(int i=1;i<=totx;i++) cnt-=tree[lson(x[i])].v;
for(int i=1;i<=toty;i++) cnt+=tree[lson(y[i])].v;
for(int i=1;i<=totx;i++) x[i]=rson(x[i]);
for(int i=1;i<=toty;i++) y[i]=rson(y[i]);
l=mid+1;
}
}
return cnt;
}
int query_more(int k,int l,int r){//查询>k的数的个数
int cnt=0;
while(l!=r){
int mid=(l+r)>>1;
if(k<=mid){
for(int i=1;i<=totx;i++) cnt-=tree[rson(x[i])].v;
for(int i=1;i<=toty;i++) cnt+=tree[rson(y[i])].v;
for(int i=1;i<=totx;i++) x[i]=lson(x[i]);
for(int i=1;i<=toty;i++) y[i]=lson(y[i]);
r=mid;
}else{
for(int i=1;i<=totx;i++) x[i]=rson(x[i]);
for(int i=1;i<=toty;i++) y[i]=rson(y[i]);
l=mid+1;
}
}
return cnt;
}
}T1; struct fenwick_tree{
inline int lowbit(int x){
return x&(-x);
}
int root[maxn+5];
void update(int t2pos,int t1pos,int val){
for(int i=t2pos;i<=n;i+=lowbit(i)){
T1.update(root[i],t1pos,val,1,n);
}
}
int query_less(int lpos,int rpos,int val){
T1.totx=T1.toty=0;
for(int i=lpos-1;i;i-=lowbit(i)) T1.x[++T1.totx]=root[i];
for(int i=rpos;i;i-=lowbit(i)) T1.y[++T1.toty]=root[i];
return T1.query_less(val,1,n);
}
int query_more(int lpos,int rpos,int val){
T1.totx=T1.toty=0;
for(int i=lpos-1;i;i-=lowbit(i)) T1.x[++T1.totx]=root[i];
for(int i=rpos;i;i-=lowbit(i)) T1.y[++T1.toty]=root[i];
return T1.query_more(val,1,n);
}
}T2; int pos[maxn+5];
long long ans=0;
int main(){
// freopen("4.in","r",stdin);
// freopen("4.out","w",stdout);
// freopen("1.txt","r",stdin);
int x;
qread(n);
qread(m);
for(int i=1;i<=n;i++){
qread(a[i]);
pos[a[i]]=i;
ans+=T2.query_more(1,i-1,a[i]);
T2.update(i,a[i],1);
}
qprint(ans);
putchar('\n');
for(int i=1;i<m;i++){
qread(x);
ans-=T2.query_more(1,pos[x]-1,x);
ans-=T2.query_less(pos[x]+1,n,x);
qprint(ans);
putchar('\n');
T2.update(pos[x],x,-1);
}
}
/*
5 4
1 5 3 4 2
5
1
4
2 */

[BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)的更多相关文章

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

    题面 传送门:[CQOI2011]动态逆序对 Solution 一开始我看到pty巨神写这套题的时候,第一眼还以为是个SB题:这不直接开倒车线段树统计就完成了吗? 然后冷静思考了一分钟,猛然发现单纯的 ...

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

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

  3. Luogu P3157 [CQOI2011]动态逆序对

    题目链接 \(Click\) \(Here\) 这个题有点卡常数..我的常数比较大所以是吸着氧气跑过去的... 题意:计算对于序列中每个位置\(p\),\([1,p-1]\)区间内比它大的数的个数,和 ...

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

    传送门 解题思路 cdq分治,将位置看做一维,修改时间看做一维,权值看做一维,然后就转化成了三维偏序,用排序+cdq+树状数组.注意算删除贡献时要做两次cdq,分别算对前面和后面的贡献. #inclu ...

  5. BZOJ - 2141 排队 (动态逆序对,区间线段树套权值线段树)

    题目链接 交换两个数的位置,只有位于两个数之间的部分会受到影响,因此只需要考虑两个数之间有多少数对a[l]和a[r]产生的贡献发生了变化即可. 感觉像是个带修改的二维偏序问题.(修改点$(x,y)$的 ...

  6. bzoj 3295 (洛谷3157、3193) [Cqoi2011]动态逆序对——树套树 / CDQ分治

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3295 题目--洛谷3157:https://www.luogu.org/problemnew ...

  7. [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)

    [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...

  8. BZOJ 3295: [Cqoi2011]动态逆序对

    3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3865  Solved: 1298[Submit][Sta ...

  9. Bzoj 3295: [Cqoi2011]动态逆序对 分块,树状数组,逆序对

    3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2886  Solved: 924[Submit][Stat ...

随机推荐

  1. 2018微信小程序开发遇到的坑

    第一个坑:wx.showModal(OBJECT) wx.showModal在安卓手机里,如果点击遮罩的话会关闭弹窗,不会有任何回调.而苹果的情况下则是点击遮罩不会有任何反应. 这样会有什么问题呢? ...

  2. Linux常用命令学习记录

    兄弟连Linux培训 ,小编整理了常用的Linux学习命令: 1 cp 拷贝命令 参数:-p 文件属性一起拷贝 -r 拷贝文件夹 -d 软链信息等一起拷贝 -a 是-rdp的简写 2 find 文件查 ...

  3. php array_keys()函数 语法

    php array_keys()函数 语法 作用:返回包含数组中所有键名的一个新数组.直线电机选型 语法:array_keys(array,value,strict) 参数: 参数 描述 array ...

  4. 向上取整&向下取整

    使用floor函数. floor(x)返回的是小于或等于x的最大整数.eg.      floor(1.5) = 1 floor(-2.5) = -3 使用ceil函数. ceil(x)返回的是大于x ...

  5. HPU personal training

    K - Two Contests 原题链接:https://agc040.contest.atcoder.jp/tasks/agc040_b?lang=en 题目大意: 给一个区间集合,将这些区间分为 ...

  6. YJJ's Salesman

    YJJ's Salesman YJJ is a salesman who has traveled through western country. YJJ is always on journey. ...

  7. [CSP-S模拟测试]:ants(回滚莫队)

    题目描述 然而贪玩的$dirty$又开始了他的第三个游戏. $dirty$抓来了$n$只蚂蚁,并且赋予每只蚂蚁不同的编号,编号从$1$到$n$.最开始,它们按某个顺序排成一列.现在$dirty$想要进 ...

  8. 转载自:StringUtils的常见方法

    转载自:https://blog.csdn.net/simple_smile_sun/article/details/51819158 注:运用StringUtils需要导入相关jar文件,commo ...

  9. Java 线程状态有哪些?

    线程状态有 5 种,新建,就绪,运行,阻塞,死亡.关系图如下: 1. 线程 start 方法执行后,并不表示该线程运行了,而是进入就绪状态,意思是随时准备运行,但是真正何时运行,是由操作系统决定的,代 ...

  10. Java 静态static 关键字作用

    静态的方法1.可以通过类名打点访问2.不能使用this关键字3.不能访问非静态的属性和方法 /* * 静态的方法: * 1.属于类的方法,可以通过类名打点访问 * 2.方法中不能使用this关键字 * ...