codeforces 414C 分治思想运用

来源:互联网 发布:c语言大小写字母转换 编辑:程序博客网 时间:2024/06/05 10:29

这个题很劲啊。搞了我一下午。。

大意是:给你一个2n长度的随意的数组,有m次查询,每次从左到右把数组依次分成长度为qi的块,将其reverse,问每次翻转之后逆序对数多少

比较难想的一道题。有些逆序对的结论很显然,设一段区间seg的逆序对数为a那么翻转这个区间之后的逆序对数为C2|seg|a当然不能有重复的数出现,如果有重复的数出现,还需要减去C2vi其中vi表示i在区间出现的次数好的有了这些结论。其实并没有什么卵用。。。递归的思路是显然的,我们仿照归并排序的思路,先把左右区间搞出来,然后在计算出左区间对右区间的贡献值。可以在n2n复杂度预处理出来。然后重点就是查询了。一开始我更新到每个节点,设置了懒惰元素。但是不是tle就是wa

之所以说这个题很劲,就是需要很抽象的想法才能搞掉。我们想想对于每一层i,设最开始在0层,那么这层有2i个小区间。每个小区间都对答案有一个独立的贡献vv表示这个小区间左半部分对其右半部分的贡献,我们把这一层的小区间贡献的和求出来为dp1[i],每当我们翻转这一层的时候,每个小区间都翻转了,但是每个小区间都是独立的,翻转就是一起翻转,于是再直接(归并倒着排)处理处翻转的和dp2[i]每次翻转就是交换dp1,dp2。再想想,翻转第i层,只会影响更大的层数,而不会影响之前的层数(这是因为翻转每个小区间,但是每个小区间的相对位置没变,而较小的层是左区间对右区间的贡献,左右相对位置没变)。

然后每次查询暴力更新最多n层,在求和输出即可

////  Created by Running Photon//  Copyright (c) 2015 Running Photon. All rights reserved.//#include <algorithm>#include <cctype>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iomanip>#include <iostream>#include <map>#include <queue>#include <string>#include <sstream>#include <set>#include <vector>#include <stack>#define ALL(x) x.begin(), x.end()#define INS(x) inserter(x, x,begin())#define ll long long#define CLR(x) memset(x, 0, sizeof x)using namespace std;const int inf = 2e9;const int MOD = 1e9 + 7;const int maxn = (1 << 20) + 10;const int maxv = 1e3 + 10;const double eps = 1e-9;ll dp1[21], dp2[21];int a[maxn], ta[maxn], tb[maxn];int n, m, tot;void build(int head, int tail, int deep) {    if(deep == n) {        dp1[deep] = dp2[deep] = 0;        return;    }    int mid = head + tail >> 1;    build(head, mid, deep+1);    build(mid, tail, deep+1);    int tota = 0, totb = 0;    for(int i = head; i < mid; i++) {        ta[tota++] = a[i];    }    for(int i = mid; i < tail; i++) {        tb[totb++] = a[i];    }    ta[tota] = tb[totb] = inf;    int la = 0, lb = 0;    ll ret = 0, retv = 0;    for(int i = head; i < tail; i++) {        if(tb[lb] <= ta[la]) lb++;        else {            retv += totb - lb;            la++;        }    }    la = lb = 0;    for(int i = head; i < tail; i++) {        if(ta[la] <= tb[lb]) a[i] = ta[la++];        else {            a[i] = tb[lb++];            ret += tota - la;        }    }    dp1[deep] += ret;    dp2[deep] += retv;}int main() {#ifdef LOCAL    freopen("C:\\Users\\Administrator\\Desktop\\in.txt", "r", stdin);    freopen("C:\\Users\\Administrator\\Desktop\\out.txt","w",stdout);#endif//  ios_base::sync_with_stdio(0);    scanf("%d", &n);    tot = 1 << n;    std::vector<int> xs;    for(int i = 0; i < tot; i++) {        scanf("%d", a + i);    }    build(0, tot, 0);    scanf("%d", &m);    while(m--) {        int q;        scanf("%d", &q);        ll sum = 0;        q = n - q;        for(int i = q; i <= n; i++) swap(dp1[i], dp2[i]);        for(int i = 0; i <= n; i++) sum += dp1[i];        printf("%lld\n", sum);    }    return 0;}
0 0