欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

BZOJ3295: [Cqoi2011]动态逆序对(cdq分治)

程序员文章站 2022-04-04 09:25:18
Description 对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删 除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数 对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数。给1到n的一 ......
Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 6912  Solved: 2438
[][][]

Description

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

Input

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

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

Source

这题已知有三种做法:

1、主席树+树状数组

2、树套树随便套

3、cdq分治

用cdq分治的话有一种非常巧妙的思路:先时间倒流,那么每次询问就转化成了求逆序对的个数

其实也不用。。

我们考虑一个数删除之后会对答案产生怎样的贡献

设当前删除了第$i$个元素,那么在$1 - i$中比它大的都要减去

在$(i + 1) - N$中比他小的都要减去

这样的话直接正着循环一遍再倒着循环一遍就行了。

用树状数组求逆序对

代码借鉴的candy大佬,写的非常妙

#include<cstdio>
#include<vector>
#include<algorithm>
#define LL long long 
using namespace std;
const int MAXN = 400001, INF = 1e9 + 10;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9'){if(c == '-')f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
struct Query {
    int t, x, val, type, id;
    bool operator < (const Query &rhs) const {
        return x == rhs.x ? val < rhs.val : x < rhs.x;
    }
}Q[MAXN], Tp[MAXN];
int N, M, a[MAXN], pos[MAXN], tim;
LL ans[MAXN];

#define lb(x) (x & -x)
int T[MAXN];
void Add(int pos, int val) {for(int i = pos; i <= N; i += lb(i)) T[i] += val;}
int Sum(int pos) {int ans = 0; for(int i = pos; i >= 1; i -= lb(i)) ans += T[i]; return ans;}

void CDQ(int l, int r) {
    if(l == r) return;
    int mid = l + r >> 1;
    for(int i = l; i <= r; i++) 
        if(Q[i].t <= mid) Add(Q[i].val, Q[i].type);
        else ans[Q[i].id] += Q[i].type * (Sum(N) - Sum(Q[i].val));
    for(int i = l; i <= r; i++) if(Q[i].t <= mid) Add(Q[i].val, -Q[i].type);
    
    for(int i = r; i >= l; i--) 
        if(Q[i].t <= mid) Add(Q[i].val, Q[i].type);
        else ans[Q[i].id] += Q[i].type * (Sum(Q[i].val - 1));
    for(int i = l; i <= r; i++)if(Q[i].t <= mid) Add(Q[i].val, -Q[i].type);
    
    int p1 = l, p2 = mid + 1;
    for(int i = l; i <= r; i++)
        if(Q[i].t <= mid) Tp[p1++] = Q[i];
        else Tp[p2++] = Q[i];
    for(int i = l; i <= r; i++) Q[i] = Tp[i];
    CDQ(l, mid); CDQ(mid + 1, r);
}
int main() {
#ifdef WIN32
    //freopen("a.in", "r", stdin);
#endif
    N = read(); M = read();
    for(int i = 1; i <= N; i++) {
        a[i] = read(); pos[a[i]] = i ;
        Q[++tim] = (Query) {tim, i, a[i], 1, 0};
    }
    for(int i = 1; i <= M; i++) {
        int x = read();
        Q[++tim] = (Query) {tim, pos[x], x, -1, i};
    }
    sort(Q + 1, Q + tim + 1);
    CDQ(1, tim);
    for(int i = 1; i <= M; i++) {
        ans[i] += ans[i - 1];
        printf("%lld\n", ans[i - 1]);
    }
    return 0;
}