主席树与带修主席树

模板

主席树
(带离散)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int N=2e5+10;
struct TREE{
	int l,r,size;
}tree[N*20];
int h[N],a[N],root[N];
int cnt;
void insert(int x,int &y,int v,int l,int r)
{
	y=++cnt;
	tree[y]=tree[x];
	tree[y].size++;
	if(l==r)
		return;
	int mid=(l+r)>>1;
	if(v>mid)
		insert(tree[x].r,tree[y].r,v,mid+1,r);
	else
		insert(tree[x].l,tree[y].l,v,l,mid);
}
int query(int x,int y,int k,int l,int r)
{
	if(l==r)
		return l;
	int tot,mid;
	tot=tree[tree[y].l].size-tree[tree[x].l].size;
	mid=(l+r)>>1;
	if(tot<k)
		return query(tree[x].r,tree[y].r,k-tot,mid+1,r);
	else
		return query(tree[x].l,tree[y].l,k,l,mid);
}
int main()
{
	int n,m,i,L,l,r,k,x;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		h[i]=a[i];
	}
	sort(h+1,h+n+1);
	L=unique(h+1,h+n+1)-h-1; 
	for(i=1;i<=n;i++)
	{
		x=lower_bound(h+1,h+L+1,a[i])-h;
		insert(root[i-1],root[i],x,1,L); 
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&l,&r,&k);
		printf("%d\n",h[query(root[l-1],root[r],k,1,L)]);
	}
	return 0;
}

带修主席树(树状数组套线段树)
注意:

  1. 空间为即O(Nlog2N)(实测1e5数据90N可过)
    模板(CQOI2011动态逆序对)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e5+10;
struct apple{
	int size,l,r;
}tree[N*90];
int root[N],a[N],vist[N],p[N],h[N];
long long ans[N];
int cnt,n;
int lowbit(int x){ return x&(-x); } 
void insert(int &x,int v,int l,int r) // 线段树插入
{
	if(!x) x=++cnt;
	tree[x].size++;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(v>mid)
		insert(tree[x].r,v,mid+1,r);
	else
		insert(tree[x].l,v,l,mid);
}
void ins(int x) //树状数组插入
{
	for(int i=x;i<=n;i+=lowbit(i))
		insert(root[i],a[x],1,n);
}
long long query(int x,int v,int l,int r) //线段树查询
{
	if(!x) return 0;
	if(r<=v) return tree[x].size;
	if(l>v) return 0;
	int mid=(l+r)>>1;
	return query(tree[x].l,v,l,mid)+query(tree[x].r,v,mid+1,r);
}
long long query(int x,int v) //树状数组查询
{
	long long now=0;
	for(int i=x;i>0;i-=lowbit(i))
		now+=query(root[i],v,1,n);
	return now;
}
long long solve(int x) 
{
	return query(n,a[x])+query(x,n)-2LL*query(x,a[x]); 
}
int main()
{
	int m,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		h[a[i]]=i;
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d",&p[i]);
		vist[h[p[i]]]=1;
	}
	long long now=0;
	for(i=1;i<=n;i++)
		if(!vist[i])
		{ 
			now+=solve(i); 
			ins(i);
		} 
	for(i=m;i>0;i--)
	{
		now+=solve(h[p[i]]);
		ans[i]=now;
		ins(h[p[i]]);
	}
	for(i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

题目

  1. CQOI2011动态逆序对
    题解
    删除操作,倒过来变成插入操作。
    与x有关的逆序对数 = x前面比x大的 + x后面比x小的 = x前面的数 - x前面比x小的 + 所有比小的 - x前面比x小的
    所以,仅需实现查找x前面比x小的数即可
    代码为上面模板
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值