【BZOJ3289】Mato的文件管理(莫队算法+树状数组)

来源:互联网 发布:科沃斯 irobot 知乎 编辑:程序博客网 时间:2024/06/01 09:48

记录一个菜逼的成长。。

题目链接

题目要求交换次数,其实就是逆序数
求逆序数,用树状数组来求。
题目没说数据范围,则离散化处理一下

考虑莫队算法
已知区间[l,r]的交换次数S

1求[l,r+1]的交换次数

假设a[r+1]=x
这个和一般的逆序数求法一样,更新树状数组,求和。
update(Rank[x],1)
S+=getSum(tot)getSum(Rank[x])(Rank[x]为离散化后的值)

2求[l1,r]的交换次数

假设a[l1]=x
因为区间的变化是区间的左端点往左移
所以每次加入一个,只要知道在这个区间里有多少个数比这个数小
所以
先求和S+=getSum(Rank[x])
再更新update(Rank[x],1)
比如有序列
1 4 2 3
已知区间[3,4],要求区间[2,4]
树状数组的值为0 1 1 0
Rank[x] = 4 ,此时求和值为2,即有两个数比4小。
然后更新树状数组的值为0 1 1 1

3求[l,r1]的交换次数

假设a[r1]=x
先更新update(Rank[x],1)
再减去相应的值S=getSum(tot)getSum(Rank[x])
比如有序列
1 4 2 3
已知区间[1,4],要求[1,3]
树状数组的值为1 1 1 1
Ran[x] = 3,更新后树状数组的值为1 1 0 1
此时getSum(tot)getSum(Rank[x])=32=1就是3的逆序数

4求[l+1,r]的交换次数

假设a[l+1]=x
先更新udpdate(Rank[x],1)
再减去相应的值S=getSum(Rank[x])
比如有序列
1 4 2 3
已知区间[2,4],要求[3,4]
树状数组的值为0 1 1 1
Rank[x] = 4,更新后树状数组的值为0 1 1 0
此时getSum(Rank[x])=2就是减少的逆序数

#include <bits/stdc++.h>using namespace std;#define lowbit(x) (x)&(-x)#define cl(a,b) memset(a,b,sizeof(a))typedef long long LL;const int maxn = 50000 + 10;int pos[maxn],rk[maxn];struct Node{    int v,id;}a[maxn];bool cmp1(Node a,Node b){    if(a.v != b.v)return a.v < b.v;    return a.id < b.id;}struct Query{    int l,r,id;    int ans;    bool operator < (const Query &a) const{        if(pos[l] != pos[a.l])return pos[l] < pos[a.l];        return r < a.r;    }}Q[maxn];bool cmp2(Query a,Query b){    return a.id < b.id;}int c[maxn];void update(int x,int v){    while(x < maxn){        c[x] += v;        x += lowbit(x);    }}int getSum(int x){    int ret = 0;    while(x > 0){        ret += c[x];        x -= lowbit(x);    }    return ret;}void solve(int n,int q){    cl(c,0);    int ans = 0;    int l = 1,r = 0;    for( int i = 1; i <= q; i++ ){        if(r < Q[i].r){            for( r = r + 1; r < Q[i].r; r++ ){                update(rk[r],1);                ans += getSum(maxn-1) - getSum(rk[r]);            }            update(rk[r],1);            ans += getSum(maxn-1) - getSum(rk[r]);        }        if(l > Q[i].l){            for(l = l - 1; l > Q[i].l; l-- ){                ans += getSum(rk[l]);                update(rk[l],1);            }            ans += getSum(rk[l]);            update(rk[l],1);        }        if(r > Q[i].r){            for( ; r > Q[i].r; r-- ){                update(rk[r],-1);                ans -= getSum(maxn-1) - getSum(rk[r]);            }        }        if(l < Q[i].l){            for( ; l < Q[i].l; l++ ){                update(rk[l],-1);                ans -= getSum(rk[l]);            }        }        Q[i].ans = ans;    }    sort(Q+1,Q+1+q,cmp2);    for( int i = 1; i <= q; i++ ) {        printf("%d\n",Q[i].ans);    }}int main(){    int n,q;    while(~scanf("%d",&n)){        for( int i = 1; i <= n; i++ ){            scanf("%d",&a[i].v);            a[i].id = i;        }        int k = sqrt(n);        for( int i = 1; i <= n; i++ ){            pos[i] = (i-1) / k + 1;//分块        }        sort(a+1,a+1+n,cmp1);        for( int i = 1; i <= n; i++ )            rk[a[i].id] = i;        scanf("%d",&q);        for( int i = 1; i <= q; i++ ){            scanf("%d%d",&Q[i].l,&Q[i].r);            Q[i].id = i;        }        sort(Q+1,Q+q+1);//对查询排序        solve(n,q);    }    return 0;}
0 0