标签: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),往前不可能合法,如果有多段合法也只需要找最近
最后对每一段冒泡即可
#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;
}
标签:space 序列 ace void 其他 fclose bit printf 操作
原文地址:https://www.cnblogs.com/gmh77/p/12791207.html