码迷,mamicode.com
首页 > 其他好文 > 详细

6573. pm

时间:2020-04-28 00:27:35      阅读:47      评论:0      收藏:0      [点我收藏+]

标签:space   序列   ace   void   其他   fclose   bit   printf   操作   

题目描述

有一个 {1,2···n} 的排列 {a1 ,a2 ···an },你需要把它变成 {1,2···n}。
你可以进行两个阶段的操作。第一阶段中,你可以重复若干次,每次任选两个相邻元素并进行交换。
第二阶段中,你可以重复若干次,每次修改一个位置上的元素。第二阶段中,你可以任意修改元素,即使中间过程中序列不再是排列仍然可行。
你需要最小化两个阶段操作的总次数,并给出一种总操作次数最小的合法方案。为了简单起见,你只需要输出第一阶段的操作,SPJ 会自动计算第二阶段的最优操作,并将操作总次数和最优总次数比较。

n ≤ 2 × 10^5

题解

一种好写且快的做法:

连i->a[i],最后会拉出来一个环

如果环的元素不相邻则直接修改(否则要破坏其他的环,不优),否则交换

交换只需要交换到环长即可停,总数不超n,所以是O(n)的

另一种比较辣鸡的做法:

根据交换的边分段,每一段之间的每条边都交换过且 值域=下标范围

那么一段的次数至少为len-1,并且如果>=len了就没有意义了,所以要刚好=len-1

值域=下标范围 这个东西可以转化成∑ai=∑i且max(ai)=max(i),如果不满足就不合法

维护∑ai-i,只需要找最近的那一段即可,因为如果最近的不行则存在max(ai)>max(i),往前不可能合法,如果有多段合法也只需要找最近

最后对每一段冒泡即可

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define ll long long
#define file
using namespace std;

int tr[800001],Tr[7100001][3],a[200001],f[200001],g[200001][2],c[200001],d[200001],n,i,j,k,l,r,len,tot,ans[200001];
ll sum[200001];
map<ll,int> ls;
map<ll,int> :: iterator I;

void copy(int t1,int t2) {Tr[t2][0]=Tr[t1][0];Tr[t2][1]=Tr[t1][1];Tr[t2][2]=Tr[t1][2];}
void New(int t,int x) {copy(Tr[t][x],len+1);Tr[t][x]=++len;}

void change(int t,int l,int r,int x,int s)
{
	int mid=(l+r)/2;
	if (l==r) {tr[t]=s;return;}
	
	if (x<=mid) change(t*2,l,mid,x,s);
	else change(t*2+1,mid+1,r,x,s);
	
	tr[t]=max(tr[t*2],tr[t*2+1]);
}
int find(int t,int l,int r,int x,int y)
{
	if (!t) return 0;
	
	int mid=(l+r)/2,ans=0,s;
	if (x<=l && r<=y) return tr[t];
	
	if (x<=mid) s=find(t*2,l,mid,x,y),ans=max(ans,s);
	if (mid<y) s=find(t*2+1,mid+1,r,x,y),ans=max(ans,s);
	
	return ans;
}

void Change(int t,int l,int r,int x)
{
	int mid=(l+r)/2;
	++Tr[t][2];
	if (l==r) return;
	
	if (x<=mid) New(t,0),Change(Tr[t][0],l,mid,x);
	else New(t,1),Change(Tr[t][1],mid+1,r,x);
}
int Find(int t,int l,int r,int x,int y)
{
	int mid=(l+r)/2,ans=0;
	if (x<=l && r<=y) return Tr[t][2];
	
	if (x<=mid && Tr[t][0]) ans+=Find(Tr[t][0],l,mid,x,y);
	if (mid<y && Tr[t][1]) ans+=Find(Tr[t][1],mid+1,r,x,y);
	
	return ans;
}

void swap(int &x,int &y) {int z=x;x=y;y=z;}

int main()
{
	freopen("pm.in","r",stdin);
	#ifdef file
	freopen("pm.out","w",stdout);
	#endif
	
	scanf("%d",&n);len=n;
	fo(i,1,n) scanf("%d",&a[i]),d[a[i]]=i,sum[i]=sum[i-1]+(a[i]-i),change(1,1,n,i,a[i]),copy(i-1,i),c[i]=c[i-1]+Find(i,1,n,a[i],n),Change(i,1,n,a[i]);
	ls.insert(pair<ll,int>(0,0));
	
	fo(i,1,n)
	{
		f[i]=f[i-1];g[i][0]=g[i-1][0];g[i][1]=g[i-1][1];
		I=ls.find(sum[i]);
		
		if (I!=ls.end())
		{
			k=I->second;
			if (find(1,1,n,k+1,i)==i && c[i]-c[k]-Find(k,1,n,i,n)*(i-k)==i-k-1)
			{
				if (f[k]+1>f[i])
				f[i]=f[k]+1,g[i][0]=k+1,g[i][1]=i;
			}
		}
		ls[sum[i]]=i;
	}
	
	l=g[n][0];r=g[n][1];
	while (l)
	{
		fo(i,l,r)
		{
			fd(j,d[i]-1,i)
			ans[++tot]=j,swap(a[j],a[j+1]),d[a[j]]=j,d[a[j+1]]=j+1;
		}
		r=g[l-1][1],l=g[l-1][0];
	}
	
	printf("%d\n",tot);
	fo(i,1,tot) printf("%d\n",ans[i]);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}

6573. pm

标签:space   序列   ace   void   其他   fclose   bit   printf   操作   

原文地址:https://www.cnblogs.com/gmh77/p/12791207.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!