树套树 ----- P1975 [国家集训队]排队(树状数组套权值线段树求动态逆序对)

解题思路:

  1. 首先我们知道交换两个数 a [ l ] 和 a [ r ] a[l]和a[r] a[l]a[r]影响到的区间是 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r1]
  2. 对于 a [ l ] a[l] a[l],我们要减去 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r1]里面比 a [ l ] a[l] a[l]大的,加上比 a [ l ] a[l] a[l]小的。
  3. 对于 a [ r ] a[r] a[r]同理反过来就好了。
  4. 如果 a [ l ] > a [ r ] : a n s + + a[l]>a[r]:ans++ a[l]>a[r]:ans++
  5. 如果 a [ r ] > a [ l ] : a n s − − a[r]>a[l]:ans-- a[r]>a[l]:ans
  6. 那么我们就要有个数据结构去查询任意区间里面比 a [ l    o r    r ] a[l\;or\;r] a[lorr]大或者小的数? → \rightarrow 权值线段树
  7. 但是 权 值 线 段 树 叶 子 节 点 只 能 维 护 值 域 权值线段树叶子节点只能维护值域 线,对于任意区间怎么办?
  8. 就是我们可以用前缀和的思想 [ l , r ] = [ 1 , r ] − [ 1 , l − 1 ] [l,r]=[1,r]-[1,l-1] [l,r]=[1,r][1,l1]
  9. 假设区间是 [ 1 , n ] [1,n] [1,n],那么我们可以开 n n n个权值线段树,第 i i i个权值线段树维护的是 [ 1 , i ] [1,i] [1,i]里面的数?
  10. 但是我们要交换 a [ l ] 和 a [ r ] a[l]和a[r] a[l]a[r]那么是不是要修改 [ l , n ] [l,n] [l,n] [ r , n ] [r,n] [r,n]里面所有的线段树?这里就是典型的一个位置修改影响后面n个区间
  11. 时间复杂度肯定吃不下!!
  12. 我们可以用树状数组求前缀和的思想:对于单点的贡献我们只修改后面的 l o g log log个位置。查询的时候把 l o g log log位置信息叠加就可以了
  13. 那么树状数组套线段树就产生了!!

AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
vector<int> lis;
int n;
int arr[maxn];

struct Segtree {
    int lson, rson, val;
}tr[maxn * 10];
int root[maxn], cnt;

inline void pushup(int rt) {
    tr[rt].val = tr[tr[rt].lson].val + tr[tr[rt].rson].val;
}

inline void update(int &rt, int l, int r, int pos, int val) {
    if(!rt) rt = ++ cnt;
    if(l == r) {
        tr[rt].val += val;
        return;
    }
    if(pos <= mid) update(tr[rt].lson,l,mid,pos,val);
    else update(tr[rt].rson,mid+1,r,pos,val);
    pushup(rt); 
} 

inline int getans(int rt, int l, int r, int posl, int posr) {
    if(!rt || !posr) return 0;
    if(l >= posl && r <= posr) return tr[rt].val;
    int ans = 0;
    if(posl <= mid) ans += getans(tr[rt].lson,l,mid,posl,posr);
    if(posr > mid) ans += getans(tr[rt].rson,mid+1,r,posl,posr);
    return ans;
}

//.........................

inline void add(int x,int pos, int val) {
    while(x < n) {
        update(root[x],1,n,pos,val);
        x += lowbit(x);
    }
}

inline int asklow(int pos, int val) {
    int ans = 0;
    while(pos) {
        ans += getans(root[pos],1,n,1,val-1);
        pos -= lowbit(pos);
    }
    return ans;
}

inline int askup(int pos, int val) {
    int ans = 0;
    while(pos) {
        ans += getans(root[pos],1,n,val+1,n);
        pos -= lowbit(pos);
    }
    return ans;
}

inline int getid(int x) {
    return lower_bound(lis.begin(),lis.end(),x) - lis.begin() + 1;
}

int main() {
    IOS;
    cin >> n;
    for(int i = 1; i <= n; ++ i) {
       cin >> arr[i];
       lis.push_back(arr[i]);
    }
    sort(lis.begin(),lis.end());
    lis.erase(unique(lis.begin(),lis.end()),lis.end());
    for(int i = 1; i <= n; ++ i) arr[i] = getid(arr[i]);
    ll ans = 0;
    for(int i = 1; i <= n; ++ i) {
        ans += askup(i-1,arr[i]);
        add(i,arr[i],1);
    }
    cout << ans << endl;
    int q;
    cin >> q;
    while(q--) {
        int l, r;
        cin >> l >> r;
        if(l > r) swap(l,r);
        ans -= asklow(r-1,arr[l]) - asklow(l,arr[l]);
        ans -= askup(r-1,arr[r]) - askup(l,arr[r]);
        ans += asklow(r-1,arr[r]) - asklow(l,arr[r]);
        ans += askup(r-1,arr[l]) - askup(l,arr[l]);
        
        if(arr[l] > arr[r]) ans --;
        if(arr[l] < arr[r]) ans ++;
        add(l,arr[l],-1);
        add(r,arr[r],-1);
        swap(arr[l],arr[r]);
        add(l,arr[l],1);
        add(r,arr[r],1);
        cout << ans << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值