清北学堂模拟赛d2t3 逆序对(pair)
题目描述
LYK最近在研究逆序对。
这个问题是这样的。
一开始LYK有一个2^n长度的数组ai。
LYK有Q次操作,每次操作都有一个参数k。表示每连续2^k长度作为一个小组。假设n=4,k=2,则a[1],a[2],a[3],a[4]为一个小组,a[5],a[6],a[7],a[8]为一个小组,a[9],a[10],a[11],a[12]为一个小组,a[13],a[14],a[15],a[16]也为一个小组。
然后LYK对于每个小组都翻转,也就是说原数组会变成a[4],a[3],a[2],a[1],a[8],a[7],a[6],a[5],a[12],a[11],a[10],a[9],a[16],a[15],a[14],a[13]。之后它想求出这2^n个数的逆序对是多少。
因此你需要输出对于每次操作,操作完后这2^n个数的逆序对有多少对。
两个数ai,aj被称为逆序对当且仅当i<j且ai>aj。
输入格式(pair.in)
第一行一个数n。
接下来一行2^n个数ai表示一开始的数组。
接下来一行一个数Q,表示操作的次数。
接下来一行Q个数,表示每次操作的参数k。
输出格式(pair.out)
Q行,表示每次操作后的答案。
输入样例
2
2 1 4 3
4
1 2 0 2
输出样例
0
6
6
0
样例解释
第一次操作,{2,1,4,3}->{1,2,3,4}
第二次操作,{1,2,3,4}->{4,3,2,1}
第三次操作,{4,3,2,1}->{4,3,2,1}
第四次操作,{4,3,2,1}->{1,2,3,4}
对于30%的数据n<=10,Q<=10。
对于50%的数据n<=10,Q<=1000。
对于80%的数据n<=10,Q<=200000。
对于100%的数据n<=17,Q<=200000,1<=ai<=2^n。
分析:比较巧妙的一道题。每次翻转操作其实就是将一个区间的逆序对个数与顺序对个数交换,那么先用归并排序求出逆序对的同时求出顺序对。接下来可以把翻转操作进行分解:假设交换翻转1234
1,2,3,4 ---> 1,2 3,4 ---> 3,4 1,2 --->4,3,2,1.对每一个子区间的逆序对进行分析,可以知道每个元素的贡献是这个元素在区间长度为1的逆序对数+区间长度为2的逆序对数+区间长度为4的逆序数......
每次修改操作只是更改了长度小于等于2^k的区间的逆序对数,对于长度大于2^k的区间就不用修改了.统计的时候把各个长度的区间答案加起来就是了.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; long long n, a[(1 << 17) + 10],q,cnt[(1 << 17) + 10][2],b[(1 << 17) + 10],k; long long ans; void Sort(int l, int r, int dep) { if (l >= r) return; int mid = (l + r) >> 1; Sort(l, mid, dep - 1); Sort(mid + 1, r, dep - 1); int i = l, j = mid + 1, res = 0; while (i <= mid && j <= r) { if (a[i] < a[j]) { cnt[dep][1] += r - j + 1; i++; } else j++; } i = l, j = mid + 1; while (i <= mid && j <= r) { if (a[i] > a[j]) { cnt[dep][0] += mid - i + 1; b[++res] = a[j++]; } else b[++res] = a[i++]; } while (i <= mid) b[++res] = a[i++]; while (j <= r) b[++res] = a[j++]; for (int i = l; i <= r; i++) a[i] = b[i - l + 1]; } int main() { scanf("%lld", &n); for (int i = 1; i <= (1 << n); i++) scanf("%lld", &a[i]); Sort(1, 1 << n, n); scanf("%lld", &q); while (q--) { scanf("%lld", &k); ans = 0; for (int i = 1; i <= n; i++) { if (i <= k) swap(cnt[i][0], cnt[i][1]); ans += cnt[i][0]; } printf("%lld\n", ans); } return 0; }