• 7381. 【2021.11.12NOIP提高组联考】转化


    Description

    ( ext{xyx}) 喜欢双射。

    他惊奇的发现, 对于所有长度为 (n) 的排列,交换每个排列在区间 ([l,r]) 中的最小值和最大值,得到的排列两两不同,这构成了一个双射!

    他很喜欢这个操作,于是他又掏出了两个排列 (left{a_{n} ight},left{b_{n} ight}),他希望用不超过 (m) 次操作来把 (left{a_{n} ight}) 变成 (left{b_{n} ight})

    Solution

    首先发现操作可逆,因此可以将题目转换成 ({a_n} ightarrow 1dots n ightarrow {b_n})。而这两步是一样的,所以到此为止,这题的目的就是利用题目给出的操作来给一个序列排序。

    再想到一个基本操作:reverse 一个有序区间,代价是 (frac{len}{2})。具体步骤是操作 ({l,r},{l+1,r-1}dots,{mid,mid+1})

    直接排序没有什么好的思路,可以用分治。

    分治到区间 ([l,r]),尝试跟归并类似的操作,先让 ([l,mid])([mid+1,r]) 有序,然后在合并两个区间。

    假设此时是这个样子:

    考虑将 ([l,mid])(le a[frac{r-l+1}{2}]) 的部分放在左边,(> a[frac{r-l+1}{2}]) 的部分放在右边,可以跟归并类似的使用双指针。

    ([l,mid]) 内找出应该放在右边的那一部分,也在 ([mid+1,r]) 内找出应该放在左边的那一部分。

    例如下图:

    将这两个部分翻转,变成这样:

    然后将两个红色的部分整体翻转,就成了这样:

    这时我们的目的也就达成了,接下来就是继续向下分治了。

    注意一个细节,就是将 (b_i) 求答案的时候,最后要讲操作反着输,因为是逆操作。

    Code

    #include<cstdio>
    #include<algorithm>
    #define N 4005
    #define M 300005
    using namespace std;
    int n,m,res,ans1,ans2,a[N],ans[M][3];
    void rever(int l,int r)
    {
    	for (int x=l,y=r;x<y;++x,--y)
    	{
    		ans[++res][1]=x;ans[res][2]=y;
    		swap(a[x],a[y]);
    	}
    }
    void Sort(int l,int r,int mid)
    {
    	if (l>=r) return;
    	int x=l-1,y=mid;
    	while (x-l+1+y-mid<(r-l+1)>>1)
    		if (y==r||(x<mid&&a[x+1]<a[y+1])) ++x;
    		else ++y;
    	rever(x+1,mid);rever(mid+1,y);
    	rever(x+1,y);
    	mid=l+(x-l+1)+(y-mid)-1;
    	Sort(l,mid,x);Sort(mid+1,r,y);
    }
    void solve(int l,int r)
    {
    	if (l>=r) return;
    	int mid=(l+r)>>1;
    	solve(l,mid);solve(mid+1,r);
    	Sort(l,r,mid);
    }
    int main()
    {
    	freopen("trans.in","r",stdin);
    	freopen("trans.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	solve(1,n);
    	for (int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	ans1=res;
    	solve(1,n);
    	ans2=res;
    	for (int i=ans1+1,j=ans2;i<=j;++i,--j)
    		swap(ans[i],ans[j]);
    	printf("%d
    ",ans2);
    	for (int i=1;i<=ans2;++i)
    		printf("%d %d
    ",ans[i][1],ans[i][2]);
    	return 0;
    }
    
  • 相关阅读:
    EventBus
    Date 时间 日期 常用方法函数
    线程 Thread Handler
    MySQL-DoubleWrite
    MySQL各版本优化器变化
    MySQL优化器-条件过滤(condition_fanout_filter)
    PXC集群搭建
    mysql主从不一致--relay_log_recovery设置成0
    MySQL5.7-sql_mode
    根据ibd文件进行数据恢复或导入
  • 原文地址:https://www.cnblogs.com/Livingston/p/15547042.html
Copyright © 2020-2023  润新知