一道线段树+树状数组的题

水上由岐有一个长为n 的序列a1; a2; : : : ; an。接下来她要进行m 次操
作。对于第k 次操作,她会指定jk,然后取出所有i ≥ jk 且ai ≤ ajk 的ai,
将它们从小到大排序后按顺序重新放回之前的位置(只有这些数的顺序可能改
变,其它数的位置不变)。
定义一个逆序对(i; j) 为满足i < j 且ai > aj 的一个二元组。第一次操作
前和每次操作结束后,她都想要知道当前序列有多少个逆序对。
任务描述
 输入
第一行,两个正整数n; m。
第二行,n 个正整数a1; a2; : : : ; an,保证1 ≤ ai ≤ n,可能存在相同值。
第三行,m 个正整数j1; j2; : : : ; jm,保证1 ≤ jk ≤ n。
输出
第一行,第一个整数表示操作前的逆序对数量,接下来m 个整数表示每次
操作后的逆序对数量。

 样例数据
 输入
6 2
1 3 4 2 6 1
2 3
输出
6 3 1
解释
第一次操作:由1 3 4 2 6 1 变为1 1 4 2 6 3。
第二次操作:由1 1 4 2 6 3 变为1 1 2 3 6 4。
 

每次重新排序的数的逆序对数变为0,而其他位置的逆序对数不改变。

所以用树状数组处理出最开始每个位置的逆序对数。(倒着for,每个位置的逆序对数:这个位置以后小于等于它的数。)

然后每次找出需重新排序的数,用线段数找区间最小值,找到后把那个位置对应的值修改为inf.

这样就再也不会找重复了。

#include<bits/stdc++.h>
using namespace std;
long long  f[800010], zhi[200005];
int n, m,a[200005],b[200005];
void read(long long &x)
{
	x = 0;
	char c = getchar();
	while(c < '0' ||c > '9')
	{
		c = getchar();
	}
	while(c < '0' && c > '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	} 
}
struct node
{
	int wei;
	long long f;
};
node q[800005];
long long qurey(long long x)
{
long long  rt = 0;
	for(int i = x; i; i -= i&-i)
	{
		rt += f[i];
	}
	return rt;
}
void modify(int a,int b)
{
	for(int i = a; i <= n; i += i&-i)
	{
		f[i] += b;
	}
}
void build(int o,int l,int r)
{
	int mid = (l + r) /2;
	q[o].f = 1e18;
	if(l == r) return;
	build(o*2,l,mid);
	build(o*2+1,mid+1,r);
}
void  update(int o)
{
	if(q[o*2].f < q[o*2+1].f)
	{
		q[o] = q[o*2];
	}
    else
		q[o] = q[o*2+1];
}
void modify1(int o, int l, int r, int pos, long long val)
{
	if(l == r)
	{
		q[o].f= val;
		q[o].wei = pos;
		return;
	}
	int mid = (l + r) >> 1;
	if(pos <= mid) modify1(o*2, l, mid, pos, val);
	if(pos > mid) modify1(o*2+1, mid + 1, r, pos, val);
    update(o);
} 
node qurey1(int o, int l, int r, int ql,int qr)
{
	if(ql <= l && r <= qr)
	{
		return q[o];
	}
	int mid = (l + r) >> 1;
	node rt, he, ha;
	rt.f = 1e18;
	if(ql <= mid)
	{
		he = qurey1(o*2,l,mid,ql,qr);
	    if(he.f < rt.f)
	    {
	    	rt = he;
	    }
	}
	if(mid < qr)
	{
		ha = qurey1(o*2+1, mid+1, r, ql, qr);
		if(ha.f < rt.f)
		{
			rt = ha;
		}
	}
	return rt; 
}
int main()
{
	long long ans = 0;
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%d%d",&n,&m);
	build(1,1,n);
	for(int i = 1; i <= n; i++)
	scanf("%lld",&a[i]);
    for(int i = n; i  >= 1; i--)
    {
    	b[i] = qurey(a[i]-1);
    	ans += b[i];
    	modify1(1,1,n,i,a[i]);
    	modify(a[i],1);
    }
    cout <<ans<< " " ;
    for(int i = 1; i <= m; i++)
    {
    	int l, cnt = 0;
		long long ans1 = 0;
    	scanf("%d",&l);
        while(1)
        {
        	node ha = qurey1(1,1,n,l,n);
        	if(ha.f > a[l]) break;
        	ans1 += b[ha.wei];
        	modify1(1,1,n,ha.wei,1e9);
        }
    	ans -= ans1;
       if(i != m)
    	printf("%lld ", ans);
       else printf("%lld",ans) ;
	}
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值