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

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 6912  Solved: 2438
[Submit][Status][Discuss]

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;
}

 

posted @ 2018-07-12 15:31  自为风月马前卒  阅读(597)  评论(0编辑  收藏  举报

Contact with me