UVA

来源:互联网 发布:linux 登录用户权限 编辑:程序博客网 时间:2024/06/04 00:48

传送门:UVA - 11990

题意:给出一个1到n的全排列A[i],并给出m个删除操作,问删除每一个数前序列中有几对逆序对。

思路:我写的方法来自:点击打开链接

很容易想到删除一个数以后,减少的逆序对来自在它前面比它大的数,和在它后面比它小的数,暴力找这两种数的话显然是不行的,找比一个数大(小)的数可以用BST(二叉搜索树)去维护,但是我们怎么维护这些数在位置上的先后关系呢,考虑使用树状数组,这时树状数组的每个节点不再是单个值,而是一个BST,位置i的BST里存的是

A[ i - lowbit(i) + 1] ... A[i] 这些数,这样树状数组求sum(i)的过程,再加上BST的查找过程,就变成了求出1 - i区间内所有小于(大于)A[i]的数的数量,将O(n)的复杂度降到了O((logn)^2)。

代码:

#include<bits/stdc++.h>#define ll long long#define pb push_backusing namespace std;typedef pair<int,int>P;const int MAXN=200010;int tot, n;ll ans;int bit[MAXN], pos[MAXN], root[MAXN];struct node{int son[2];int sz, val;bool tag; // tag == 0 表示该结点已删除 node(){}node(int _val) : val(_val){tag = sz = 1;son[0] = son[1] = 0;}}tree[MAXN * 20];int sum(int i){int res = 0;while(i){res += bit[i];i -= i & -i;}return res;}void add(int i, int x){while(i < MAXN){bit[i] += x;i += i & -i;}}int new_node(int val){tree[++tot] = node(val);return tot;}int build(vector<int> &v, int l, int r){int mid = (l + r) >> 1, rt = new_node(v[mid - 1]);if(l < mid) tree[rt].son[0] = build(v, l, mid - 1);if(r > mid) tree[rt].son[1] = build(v, mid + 1, r);tree[rt].sz += tree[tree[rt].son[0]].sz + tree[tree[rt].son[1]].sz;return rt;}int query(int rt, int x){if(!rt) return 0;if(tree[rt].val == x) return tree[tree[rt].son[0]].sz + tree[rt].tag;if(tree[rt].val < x) return tree[tree[rt].son[0]].sz + tree[rt].tag + query(tree[rt].son[1], x);return query(tree[rt].son[0], x);}void del(int rt, int x){tree[rt].sz--;if(x < tree[rt].val) del(tree[rt].son[0], x);else if(x > tree[rt].val) del(tree[rt].son[1], x);else tree[rt].tag = 0;}vector<int> v[MAXN];void init(){memset(bit, 0, sizeof(int) * (n + 5));for(int i = 0; i <= n; i++) v[i].clear();tot = ans = tree[0].sz = 0; }void remove(int x){int pre, suf;pre = suf = 0;for(int i = pos[x]; i; i -= i & -i){int tmp = query(root[i], x);suf += tmp;pre += tree[root[i]].sz - tmp;}suf = sum(x) - suf;ans -= (pre + suf);add(x, -1);for(int i = pos[x]; i <= n; i += i & -i)del(root[i], x);}int main(){int m, t;while(cin >> n >> m){init();for(int i = 1; i <= n; i++){scanf("%d", &t);ans += i - 1 - sum(t);add(t, 1);pos[t] = i;for(int j = i; j <= n; j += j & -j)v[j].pb(t);}for(int i = 1; i <= n; i++){sort(v[i].begin(), v[i].end());root[i] = build(v[i], 1, v[i].size());}while(m--){scanf("%d", &t);printf("%lld\n", ans);remove(t);}} return 0;}

这题还有一种思维很巧妙的解法是二维块状数组,思路见:点击打开链接  点击打开链接

不过由于涉及到xoy坐标系和二维矩阵之间横纵坐标的转化,因此看代码有点迷,这种方法还有点容斥操作。


最后一种解法是cdq分治,不过蒟蒻还没学,先放一放吧。。

原创粉丝点击