bzoj3295 [Cqoi2011]动态逆序对

3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 6119  Solved: 2142
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1

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

HINT

N<=100000 M<=50000

分析:cdq分治比较裸的题目.考虑i,j如何才能构成逆序对:Ti < Tj,Xi < Xj,Yi > Yj.其中T是cdq分治中的时间,X是下标,Y是大小.因为题目只涉及到删除操作,那么可以用一个很常用的方法:时间倒流来处理.每增加一个点,看T ≤ mid的点中有多少x比它大并且y比它小的点,和x比它小并且y比它大的点.因为每次不光是要计算原先被删除的点构成的逆序对,还要算序列中剩下的数的逆序对.那么一开始将序列初始化为空序列,对每一个元素分配一个T,代表插入的时间,其中没有被删除的点要先分配,这样就能做到统计答案不遗漏了.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;
const int maxn = 100010;
int n,m,pos[maxn],T;
ll ans[maxn],c[maxn],Ans;

struct node
{
    int id,x,y;
}e[maxn],p[maxn];

bool cmp(node a,node b)
{
    if (a.x == b.x && a.y == b.y)
        return a.id < b.id;
    if (a.x == b.x)
        return a.y < b.y;
    return a.x < b.x;
}

void add(int x,int v)
{
    while (x <= n)
    {
        c[x] += v;
        x += x & (-x);
    }
}

ll query(int x)
{
    ll res = 0;
    while (x)
    {
        res += c[x];
        x -= x & (-x);
    }
    return res;
}

void solve(int l,int r)
{
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    for (int i = l; i <= r; i++)
        {
            if (e[i].id <= mid)
                add(e[i].y,1);
            else
                ans[e[i].id] += query(n) - query(e[i].y);
        }
    for (int i = l; i <= r; i++)
        if (e[i].id <= mid)
            add(e[i].y,-1);
    for (int i = r; i >= l; i--)
    {
        if (e[i].id <= mid)
            add(e[i].y,1);
        else
            ans[e[i].id] += query(e[i].y);
    }
    for (int i = r; i >= l; i--)
        if (e[i].id <= mid)
            add(e[i].y,-1);
    int L = l,R = mid + 1;
    for (int i = l; i <= r; i++)
    {
        if (e[i].id <= mid)
            p[L++] = e[i];
        else
            p[R++] = e[i];
    }
    for (int i = l; i <= r; i++)
        e[i] = p[i];
    solve(l,mid);
    solve(mid + 1,r);
}

int main()
{
    scanf("%d%d",&n,&m);
    T = n;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d",&e[i].y);
        e[i].x = i;
        pos[e[i].y] = i;
    }
    for (int i = 1; i <= m; i++)
    {
        int k;
        scanf("%d",&k);
        e[pos[k]].id = T--;
    }
    for (int i = 1; i <= n; i++)
        if (!e[i].id)
            e[i].id = T--;
    sort(e + 1,e + 1 + n,cmp);
    solve(1,n);
    for (int i = 1; i <= n; i++)
        Ans += ans[i];
    for (int i = n; i > n - m; i--)
    {
        printf("%lld\n",Ans);
        Ans -= ans[i];
    }

    return 0;
}

 

posted @ 2018-01-06 12:21  zbtrs  阅读(246)  评论(0编辑  收藏  举报