题解-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];

void add(int &v,int xx,int l,int r){
    if(!v)
        v=++cnt;
    ++x[v];
    if(l==r)
        return;
    int mid=(l+r)>>1;
    if(xx<=mid)
        add(c[v][0],xx,l,mid);
    else
        add(c[v][1],xx,mid+1,r);
}

int ct(int &v,int xx,int yy,int l,int r){
    if(!v)
        return 0;
    if(xx<=l&&r<=yy)
        return x[v];
    int mid=(l+r)>>1,s=0;
    if(xx<=mid)
        s+=ct(c[v][0],xx,yy,l,mid);
    if(mid<yy)
        s+=ct(c[v][1],xx,yy,mid+1,r);
    return s;
}

int n;

void ins(int p,int a){
    int i;
    for(i=p;i<=n;i+=-i&i)
        add(r[i],a,1,n);
}

int clc(int p,int a){
    int i,s=0;
    for(i=p;i;i-=-i&i)
        s+=ct(r[i],a+1,n,1,n);
    for(i=n;i;i-=-i&i)
        s+=ct(r[i],1,a-1,1,n);
    for(i=p;i;i-=-i&i)
        s-=ct(r[i],1,a-1,1,n);
    return s;
}

int main()
{
    int m,i;
    i64 s=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        p[a[i]]=i;
    }
    for(i=1;i<=m;i++){
        scanf("%d",&q[i]);
        b[p[q[i]]]=1;
    }
    for(i=1;i<=n;i++)
        if(!b[i]){
            s+=clc(i,a[i]);
            ins(i,a[i]);
        }
    for(i=m;i;i--){
        s+=clc(p[q[i]],q[i]);
        ins(p[q[i]],q[i]);
        ans[i]=s;
    }
    for(i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    return 0;
}

用cdq分治更好。

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long i64;
const int N=1e5+5;

i64 ans[N];
struct inf{
    int p,t;
    i64 *q;
}x[N],y[N];
int n;
int bt[N],mt[N];
int a[N],q[N];

void add(int t){
    for(;t<=n;t+=t&-t)
        ++bt[t];
}

void dec(int t){
    for(;t<=n;t+=t&-t)
        --bt[t];
}

int ask(int t){
    int s=0;
    for(;t;t-=t&-t)
        s+=bt[t];
    return s;
}

void _add(int t){
    for(;t<=n;t+=t&-t)
        ++mt[t];
}

void _dec(int t){
    for(;t<=n;t+=t&-t)
        --mt[t];
}

int _ask(int t){
    int s=0;
    for(;t;t-=t&-t)
        s+=mt[t];
    return s;
}

void cdqdc(int l,int r){
    if(l==r)
        return;
    int mid=(l+r)>>1;
    cdqdc(l,mid);
    cdqdc(mid+1,r);
    int i,j,p;
    for(i=l;i<=mid;i++)
        _add(x[i].t);
    for(i=l,j=mid+1,p=l;i<=mid||j<=r;p++)
        if(j>r||i<=mid&&x[i].p<x[j].p){
            *x[i].q+=ask(x[i].t);
            _dec(x[i].t);
            y[p]=x[i++];
        }
        else{
            add(x[j].t);
            *x[j].q+=_ask(x[j].t);
            y[p]=x[j++];
        }
    for(j=mid+1;j<=r;j++)
        dec(x[j].t);
    for(p=l;p<=r;p++)
        x[p]=y[p];
}

int main()
{
    int m,i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        x[a[i]].p=i;
        x[a[i]].q=ans+m+1;
    }
    for(i=1;i<=m;i++){
        scanf("%d",&q[i]);
        x[q[i]].t=n-i+1;
        x[q[i]].q=ans+i;
    }
    int s=0;
    for(i=1;i<=n;i++)
        if(!x[i].t)
            x[i].t=++s;
    cdqdc(1,n);
    for(i=m;i;i--)
        ans[i]+=ans[i+1];
    for(i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/wanghaoyu/p/Solution2.html

时间: 2024-09-30 11:43:15

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

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

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

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\)与元

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

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