首页 > 技术文章 > JZOJ 5222. 【GDOI2018模拟7.12】A(权值线段树)

LZA119 2018-12-03 21:56 原文

JZOJ 5222. 【GDOI2018模拟7.12】A

题目

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

3 2
2 3 1
1
1

Sample Output

2
1
1

Data Constraint

在这里插入图片描述

Hint

在这里插入图片描述

题解

这题要用到权值线段树
首先,我们可以发现一个重要的性质:
设以 i i i为左端点的逆序对个数为 s [ i ] s[i] s[i]
每个操作过的位置会使它自己和它后面 I Q IQ IQ值小于他的点的 s [ i ] s[i] s[i]都变为 0 0 0
因此,可以统计每个位置的 s [ i ] s[i] s[i]最早变为 0 0 0的时刻,即可轻松统计每个时刻的逆序对。
先把 m m m次操作的位置枚举一遍,令 t [ i ] t[i] t[i]为位置 i i i最早被操作的时间。
一个位置 i i i所对应的 s [ i ] s[i] s[i]变为 0 0 0的最早时刻,即为它或它前面比它 I Q IQ IQ值大的点最早被操作的时刻。
不难想到,用建立一棵权值线段树,
将读入的 I Q IQ IQ序列从左到右枚举一遍,进行如下两个操作:
1、用 t [ i ] t[i] t[i]更新权值线段树上对应的表示 I Q [ i ] IQ[i] IQ[i]的点(如果更小才更新)
2、在权值为 [ I Q [ i ] , m a x ] [IQ[i],max] [IQ[i],max]的区间内找到最小的数,即为该点的清零时刻 t i m e time time
自然地,把 s u m [ t i m e ] sum[time] sum[time]加上 s [ i ] s[i] s[i]
最后 a n s ans ans先为总的逆序对个数,
每到一个时刻 i i i就减去 s u m [ i ] sum[i] sum[i],再输出。

代码

#include<cstdio>
#include<cstring>
using namespace std;
#define N 100010
int ss,a[N],p[N],s[N],h[N],t[N],f[N*5];
int last[N],to[N],next[N],len=0;
struct
{
	int id,x;
}b[N],c[N],d[N];
void gb(int l,int r)
{
	int i;
	if(l==r) return;
	else
	{
		int mid=(l+r)/2,cc,dd;
		gb(l,mid),gb(mid+1,r);
		for(i=l;i<=mid;i++) c[i-l+1]=b[i];cc=mid-l+1;
		for(i=mid+1;i<=r;i++) d[i-mid]=b[i];dd=r-mid;
		int x=1,y=1,t=1;
		while(x<=cc||y<=dd)
		{
			if(x>cc) b[l+t-1]=d[y],y++;
			else if(y>dd) b[l+t-1]=c[x],s[c[x].id]+=dd,x++;
			else if(c[x].x<=d[y].x) b[l+t-1]=c[x],s[c[x].id]+=y-1,x++; else b[l+t-1]=d[y],y++;
			t++;
		}
	}
}
void change(int k,int l,int r,int x,int y)
{
	if(l==r) 
	{
		if(y<f[k]) f[k]=y;
	}
	else
	{
		int mid=(l+r)/2;
		if(x<=mid) change(k*2,l,mid,x,y); else change(k*2+1,mid+1,r,x,y);
		if(f[k*2]<f[k*2+1]) f[k]=f[k*2]; else f[k]=f[k*2+1];
	}
}
void find(int k,int l,int r,int x,int y)
{
	if(l==x&&r==y) 
	{
		if(f[k]<ss) ss=f[k];
	}
	else
	{
		int mid=(l+r)/2;
		if(y<=mid) find(k*2,l,mid,x,y);
		else if(x>mid) find(k*2+1,mid+1,r,x,y);
		else
		{
			find(k*2,l,mid,x,mid);
			find(k*2+1,mid+1,r,mid+1,y);
		}
	}
}
void add(int x,int y)
{
	to[++len]=y;
	next[len]=last[x];
	last[x]=len;
}
int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	int n,m,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) scanf("%d",&a[i]),b[i].id=i,b[i].x=a[i];
	gb(1,n);
	b[0].x=-1;
	for(i=1;i<=n;i++) 
	{
		if(b[i].x!=b[i-1].x) h[++h[0]]=b[i].x;
		p[b[i].id]=h[0];
	}
	memset(t,0,sizeof(t));
	memset(f,127,sizeof(f));
	for(i=1;i<=m;i++) 
	{
		scanf("%d",&c[i].x),c[i].id=i;
		if(t[c[i].x]==0) t[c[i].x]=i;
	}
	for(i=1;i<=n;i++)
	{
		if(t[i]!=0) change(1,1,h[0],p[i],t[i]);
		ss=2147483647;
		find(1,1,h[0],p[i],h[0]);
		if(ss<=m) add(ss,i);
	}
	long long ans=0;
	for(i=1;i<=n;i++) ans+=s[i];
	printf("%lld\n",ans);
	for(i=1;i<=m;i++)
	{
		for(int j=last[i];j;j=next[j]) ans-=s[to[j]];
		printf("%lld\n",ans);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}

推荐阅读