JZOJ5947. 【NOIP2018模拟11.02】初音未来(miku)

Description

Hercier作为一位喜爱Hatsune Miku的OIer,痛下决心,将Vocaloid买回了家。打开之后,你发现界面是一个长为n的序列,代表音调,并形成了全排列。你看不懂日语,经过多次尝试,你只会用一个按钮:将一段区间按升序排序。不理解音乐的Hercier决定写一个脚本,进行m次操作,每次对一段区间进行操作。可惜Hercier不会写脚本,他找到了在机房里的你,请你模拟出最后的结果。
在这里插入图片描述

题解

这里的m非常的大,而且n很小。
对于70%,是很简单的,直接二分一个答案,
然后变成0/1的形式,用线段树来维护1的个数,
排序就直接区间修改。
而对于100%就不能这样做了。
对于一个任意的全排列,它的逆序对个数,是 n 2 n^2 n2级别的,
而将任意一个全排列排序交换的个数就是逆序对个数,
也就是说最多的有效交换只有不到 n 2 n^2 n2次。
用一个线段树维护一下这个位置与前面的位置是否构成逆序对,
如果有就直接交换,
这样排序的次数就非常少了。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define ls (x<<1)
#define rs (x<<1|1)
#define M ((l+r)>>1)
using namespace std;
int n,a[2003],m,stl,str,L,R;
int opl,opr,ops,id,z[13],lim;
bool bz[8003],s[8003];
char ch;
void read(int&n)
{
	for(ch=getchar();ch<'0'|| ch>'9';ch=getchar());
	for(n=0;'0'<=ch && ch<='9';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
}
void build(int x,int l,int r)
{
	bz[x]=0;
	if(l==r)
	{
		s[x]=(a[l]>a[l+1]);
		return;
	}
	int m=M;
	build(ls,l,m);
	build(rs,m+1,r);
	s[x]=s[ls]|s[rs];
}
void down(int x)
{
	if(bz[x])bz[ls]=bz[rs]=1,s[ls]=s[rs]=bz[x]=0;
}
void solve(int x,int l,int r)
{
	down(x);
	if(l==r)
	{
		for(int j=l;j>=L && a[j]>a[j+1];j--)
			for(int i=j;i<R && a[i]>a[i+1];i++)swap(a[i],a[i+1]);
		return;
	}
	int m=M;
	if(s[rs])solve(rs,m+1,r);
	if(s[ls])solve(ls,l,m);
}
void find(int x,int l,int r)
{
	down(x);
	if(s[x]==0)return;
	if(opl<=l && r<=opr)
	{
		solve(x,l,r);
		return;
	}
	int m=M;
	if(m<opr)find(rs,m+1,r);
	if(opl<=m)find(ls,l,m);
}
void work(int x,int l,int r)
{
	down(x);
	if(s[x]==0)return;
	if(opl<=l && r<=opr)
	{
		bz[x]=1;s[x]=0;
		return;
	}
	int m=M;
	if(opl<=m)work(ls,l,m);
	if(m<opr)work(rs,m+1,r);
	s[x]=s[ls]|s[rs];
}
void ins(int x,int l,int r)
{
	down(x);
	if(l==r)
	{
		s[x]=1;
		return;
	}
	int m=M;
	if(opl<=m)ins(ls,l,m);else ins(rs,m+1,r);
	s[x]=s[ls]|s[rs];
}
int main()
{
	freopen("miku.in","r",stdin);
	freopen("miku.out","w",stdout);
	read(n);read(m);read(stl);read(str);
	for(int i=1;i<=n;i++)read(a[i]);a[n+1]=n+1;
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		read(L);read(R);
		opl=L;opr=R;
		find(1,1,n);
		work(1,1,n);
		if(a[L-1]>a[L])opl=L-1,ins(1,1,n);
		if(a[R]>a[R+1])opl=R,ins(1,1,n);
	}
	for(int i=stl;i<=str;i++)printf("%d ",a[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值