[CQOI2011]动态逆序对(主席树,树状数组)

[CQOI2011]动态逆序对(luogu)

题目描述

对于序列 aa,它的逆序对数定义为集合

\{(i,j)| i<j \wedge a_i > a_j \}{(i,j)∣i<j∧ai?>aj?}

中的元素个数。

现在给出 1\sim n1∼n 的一个排列,按照某种顺序依次删除 mm 个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

输入格式

第一行包含两个整数 nn 和 mm,即初始元素的个数和删除的元素个数。
以下 nn 行,每行包含一个 1 \sim n1∼n 之间的正整数,即初始排列。
接下来 mm 行,每行一个正整数,依次为每次删除的元素。

输出格式

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

Solution

(其实我觉得更像是一个动态开点线段树啊,为什么归到主席树里???等我学了那东西再回来看看吧)

首先用树状数组求逆序对(正着来一次反着来一次)

a1[ d[ i ] ]:编号在 i 前且值比 d[ i ]小的个数

a2[ d[ i ] ]:编号在 i 后且值比 d[ i ] 的值大的个数

然后每次删去一个数t减去a1[ t ]+a2[ t ]

聪明的你肯定马上发现减多了吧(正反都会减一次)

这时用主席树维护已经删去的数里跟它构成逆序对的个数

即删去 t 时比它所在编号(re[ t ])大的编号的权值线段树里 t 加1(自然语言好无力。。。)

为了减小复杂度此处用树状数组优化

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#define ll long long
using namespace std;
const int N=1e5+10;
ll s_t[N],a1[N],a2[N],sum;
int n,m,t,d[N],re[N];
int lowbit(int x)
{
    return x&(-x);
}
ll s_get(int x,int y)
{
    ll ans=0;
    for(;y;y-=lowbit(y)) ans+=s_t[y];
    for(;x;x-=lowbit(x)) ans-=s_t[x];
    return ans;
}
void s_add(int x)
{
    for(;x<=n;x+=lowbit(x)) s_t[x]++;
}
int rt[N],tot;
struct node
{
    int lc,rc,v;
}f[N*60];
void z_add(int &g,int l,int r,int pos)
{
    if(!g) g=++tot;
    f[g].v++;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(pos<=mid) z_add(f[g].lc,l,mid,pos);
    else z_add(f[g].rc,mid+1,r,pos);
}
ll z_get(int g,int l,int r,int x,int y)
{
    if(x>y || g==0) return 0;
    if(l>=x && r<=y) return f[g].v;
    int mid=(l+r)>>1;
    if(y<=mid) return z_get(f[g].lc,l,mid,x,y);
    else if(x>mid) return z_get(f[g].rc,mid+1,r,x,y);
    else return z_get(f[g].lc,l,mid,x,mid)+z_get(f[g].rc,mid+1,r,mid+1,y);
}
ll Get(int x,int y,int l,int r)
{
    if(l>r) return 0;
    ll ans=0;
    for(;y;y-=lowbit(y)) ans+=z_get(rt[y],1,n,l,r);
    for(;x;x-=lowbit(x)) ans-=z_get(rt[x],1,n,l,r);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&d[i]);
        re[d[i]]=i;
        a1[d[i]]=s_get(d[i],n);
        s_add(d[i]),sum+=a1[d[i]];
    }
    memset(s_t,0,sizeof(s_t));
    for(int i=n;i;i--)
        a2[d[i]]=s_get(0,d[i]),s_add(d[i]);
    while(m--)
    {
        scanf("%d",&t);
        printf("%lld\n",sum);
        sum-=a1[t]+a2[t]-Get(0,re[t],t+1,n)-Get(re[t],n,1,t-1);
        for(int i=re[t];i<=n;i+=lowbit(i))
            z_add(rt[i],1,n,t);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/hsez-cyx/p/12269710.html

时间: 2024-10-03 08:31:08

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

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

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

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行每行一个正整数,依次

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

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

题解 P3157 【[CQOI2011]动态逆序对】

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

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

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

BZOJ3295 CQOI2011 动态逆序对 树状数组套线段树

离线倒着做,每次加入一个节点后新增的逆序对数量就是其左边大于它的数的个数(左边数的总数-左边小于它的数的个数)+右边小于它的数的个数 用树状数组维护求和,对于树状数组中每个节点v所对应的区间线段树维护区间[l,r]中大于v的数的个数. 最后唯一的问题就是指针版线段树MLE-- #include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <alg