BZOJ3295: [Cqoi2011]动态逆序对(cdq分治)
程序员文章站
2022-04-04 09:25:18
Description 对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删 除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数 对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数。给1到n的一 ......
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
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)。
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; }