【线段树】【数据结构】四校联考1024T3

本文介绍了一种利用线段树和树状数组优化逆序对计数的算法,通过重新定义逆序对的概念,实现了每次排序后点的贡献更新,避免了重复计算,有效提高了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

在这里插入图片描述


分析:

没过是因为没看。。。

这题其实相当水。。。

重新定义一下逆序对:每个点的贡献为,其后面的,比它小的数的个数。

然后这样一来,每次排过序之后的点,其后面就不可能有比它小的值了,直接忽略以后排序又排到它的情况。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 200010
#define INF 0x3FFFFFFF
using namespace std;
typedef long long ll;
int n,m;
int a[MAXN];
ll f[MAXN],ans;
ll tr[MAXN*4];
void build(int l=1,int r=n,int id=1){
	int mid=(l+r)>>1;
	if(l==r){
		tr[id]=a[l];
		return ;	
	}
	build(l,mid,id<<1);
	build(mid+1,r,id<<1|1);
	tr[id]=min(tr[id<<1],tr[id<<1|1]);
}
ll tree[MAXN];
ll find(int x){
	int res=0;
	while(x){
		res+=tree[x];
		x-=x&(-x);
	}
	return res;
}
void add(int x){
	while(x<=n){
		tree[x]++;
		x+=x&(-x);
	}
}
int find(int l1,int r1,int val,int l=1,int r=n,int id=1){
	if(tr[id]>val)
		return -1;
	if(l==r){
		tr[id]=INF;
		return l;
	}
	int res=-1;
	int mid=(l+r)>>1;
	if(l1<=mid){
		res=find(l1,r1,val,l,mid,id<<1);
		tr[id]=min(tr[id<<1],tr[id<<1|1]);
		if(res!=-1)
			return res;
	}
	if(r1>mid){
		res=find(l1,r1,val,mid+1,r,id<<1|1);
		tr[id]=min(tr[id<<1],tr[id<<1|1]);
		if(res!=-1)
			return res;	
	}
	return res;
}
int main(){	
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	SF("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		SF("%d",&a[i]);
	for(int i=n;i>=1;i--){
		if(a[i]!=1)
			f[i]=find(a[i]-1);
		ans+=f[i];
		add(a[i]);	
	}
	build();
	PF("%lld ",ans);
	int t;
	for(int i=1;i<=m;i++){
		SF("%d",&t);
		int id=find(t,n,a[t]);
		while(id!=-1){
			ans-=f[id];
			id=find(t,n,a[t]);
		}
		PF("%lld ",ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值