bzoj3333. 排队计划

sloj bzoj3333. 排队计划

题目大意

杀狗

让你不断交换几个人的位置被并求出每次交换后逆序对个数

数据范围

$ 1≤n≤500000,1≤m≤500000,1≤h_i≤10^9 $


题解

对于在\(p\)之前的位置,与其产生的逆序对个数是不会改变的

而在\(p\)之后没有被选出的数,与其产生的逆序对个数也不会改变

因为对于在\(p\)之前或之后的数,\(p\)相对于它们的位置没改变

被选出的数,它们之间的逆序对经过排序后就都消除了

减少的逆序对个数:每个被选出的数字之后比它小的数的个数

建立一个线段树,每个结点记录原序列该位置上的数

同时统计它和后面的数形成的逆序对个数(在它后面比它小的数的个数)

每次询问,我们从\(p−n\)中不断找到最小值,减去在它之后且比它小的数的个数

因为把这些元素排过序后就不会与之后的数形成逆序对了

也就是说不会再影响答案了

所以我们直接把这个位置赋值成\(INF\)即可

#include<bits/stdc++.h>
#define ll long long
#define INF 1000000000
using namespace std;
int c[500010],n,nn,m,a[500010],V[500010],MN,ID;
ll sum[500010],ans = 0;
struct node{
    int mn,id;
}t[2000010];
void add(int x){
	for(int i = x;i<=nn;i+=(i&(-i))) c[i]++;
}
ll ask(int x){
	ll ans = 0;
	for (int i = x;i>0;i-=(i&(-i))) ans+=(ll)c[i];
	return ans;
}
void update(int p){
    if (t[p<<1].mn<t[p<<1|1].mn){
    	t[p].mn = t[p<<1].mn;
		t[p].id = t[p<<1].id;
	}else{
		t[p].mn = t[p<<1|1].mn;
		t[p].id = t[p<<1|1].id;
	}
}
void build(int p,int l,int r){
    t[p].mn = INF;
    if (l==r){
        t[p].id = l;
		t[p].mn = a[l];
        return;
    }
    int mid = (l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    update(p);
}
void ask(int p,int l,int r,int x,int y){
    if(l>=x&&r<=y){
        if(t[p].mn<MN){
        	MN = t[p].mn;
			ID = t[p].id;
		}
        return;
    }
    int mid = (l+r)>>1;
    if(x<=mid) ask(p<<1,l,mid,x,y);
    if(y>mid) ask(p<<1|1,mid+1,r,x,y);
}
void change(int p,int l,int r,int x){
    if(l==r){
        t[p].mn = INF;
		t[p].id = 0;
        return;
    }
    int mid = (l+r)>>1;
    if(x<=mid) change(p<<1,l,mid,x);
    else change(p<<1|1,mid+1,r,x);
    update(p);
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i = 1;i<=n;i++){
    	scanf("%d",&a[i]);
		V[i] = a[i];
	}  
    sort(V+1,V+1+n);
    nn = unique(V+1,V+1+n)-V-1;
    for (int i = n;i>=1;i--){
        int x = lower_bound(V+1,V+1+nn,a[i])-V;
        add(x);
        sum[i] = (ll)ask(x-1); ans+=(ll)sum[i];
        a[i] = x;
    }
    printf("%lld\n",ans);
    build(1,1,n);
    for (int i = 1;i<=m;i++){
        int x;
        scanf("%d",&x);
        MN = INF,ID = 0;
        ask(1,1,n,x,n);
        while (MN<=a[x]){
            ans-=(ll)sum[ID];
            sum[ID] = 0;
            change(1,1,n,ID);
            MN = INF,ID = 0;    
            ask(1,1,n,x,n);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2023-02-08 08:16  cztq  阅读(7)  评论(0编辑  收藏  举报