【BZOJ3333】排队计划 树状数组+线段树

【BZOJ3333】排队计划

Description

Input

Output

Sample Input

6 2
160 163 164 161 167 160
2
3

Sample Output

6
3
1

HINT

题解:本题的思路比较好,可惜我只想到了一半。

先用树状数组求出f[i],代表i和后面的人能构成多少逆序对。然后我们发现,每次放哨时,只有出列的那些同学的f值会发生变化(变成0),而其他人的f值不发生改变,所以我们将这些出列的同学找出来暴力清零即可。

但是放哨后高度的排列顺序会变啊,这样会不会对其他询问有影响呢?显然不会啊!f值都清零了还会有什么影响?所以我们可以用线段树维护区间最大值,这样就能找到每个人后面所有比它小的了。

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lson x<<1
#define rson x<<1|1
using namespace std;
typedef long long ll;
const int maxn=500010;
struct node
{
	int val,org;
}p[maxn];
int n,m,nm;
ll ans;
int f[maxn],s[maxn],v[maxn],ps[maxn<<2];
bool cmp(const node &a,const node &b)
{
	return a.val<b.val;
}
inline void updata(int x)
{
	for(int i=x;i<=nm;i+=i&-i)	s[i]++;
}
inline int query(int x)
{
	int i,ret=0;
	for(i=x;i;i-=i&-i)	ret+=s[i];
	return ret;
}
inline int MX(int a,int b)
{
	return v[a]<v[b]?a:b;
}
void build(int l,int r,int x)
{
	if(l==r)
	{
		ps[x]=l;
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,lson),build(mid+1,r,rson);
	ps[x]=MX(ps[lson],ps[rson]);
}
int query(int l,int r,int x,int a,int b)
{
	if(a<=l&&r<=b)	return ps[x];
	int mid=(l+r)>>1;
	if(b<=mid)	return query(l,mid,lson,a,b);
	if(a>mid)	return query(mid+1,r,rson,a,b);
	return MX(query(l,mid,lson,a,b),query(mid+1,r,rson,a,b));
}
void updata(int l,int r,int x,int a)
{
	if(l==r)	return ;
	int mid=(l+r)>>1;
	if(a<=mid)	updata(l,mid,lson,a);
	else	updata(mid+1,r,rson,a);
	ps[x]=MX(ps[lson],ps[rson]);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	int i,a,b,c;
	for(i=1;i<=n;i++)	p[i].val=rd(),p[i].org=i;
	sort(p+1,p+n+1,cmp);
	for(i=1;i<=n;i++)
	{
		if(p[i].val>p[i-1].val)	nm++;
		v[p[i].org]=nm;
	}
	for(i=n;i;i--)	f[i]=query(v[i]-1),ans+=f[i],updata(v[i]);
	build(1,n,1);
	printf("%lld\n",ans);
	for(i=1;i<=m;i++)
	{
		a=rd(),c=v[a];
		if(c!=1<<30)
		{
			while(1)
			{
				b=query(1,n,1,a,n);
				if(v[b]>c)	break;
				ans-=f[b],v[b]=1<<30,updata(1,n,1,b);
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}//6 2 160 163 164 161 167 160 2 3 

 

posted @ 2017-09-17 15:20  CQzhangyu  阅读(319)  评论(0编辑  收藏  举报