codeforces1474E What Is It?

题目链接

题目:

有一个排列 p p p,有一种操作:选择两个数 i , j i,j i,j,满足 i ≠ j , p j = i i \ne j,p_j=i i=j,pj=i,然后 s w a p ( p i , p j ) swap(p_i,p_j) swap(pi,pj),代价为 ( j − i ) 2 (j-i)^2 (ji)2。不停地对排列 p p p做以上操作,直到满足 p i = i , ∀ i ∈ [ 1 , n ] p_i=i, \forall i \in [1,n] pi=i,i[1,n]
要你给出一个排列 p p p,和相对应的操作序列,使代价最大。
( 2 ≤ n ≤ 1 0 5 ) (2 \le n \le 10^5) (2n105)

题解:

我一开始想的是对于每一种排列都有一种固定的贪心方法,然后我们再贪出代价最大的排列,把问题想的很复杂。
这道题可以逆过来做,考虑原排列为 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n,我们有一个操作:选择一个 i i i满足 p i = i p_i=i pi=i,然后选择任意一个 j j j满足 j ≠ i j \ne i j=i,然后 s w a p ( p i , p j ) swap(p_i,p_j) swap(pi,pj),代价为 ( j − i ) 2 (j-i)^2 (ji)2。然后我们用这个操作将原排列乱序,直至没有数在原来的位置上了为止。显然我们最多可以操作 n − 1 n-1 n1次,因为第一次操作肯定是两个在原来位置上的数互换。考虑每个位置可以产生的最大贡献,1和 n n n最大可以产生 ( n − 1 ) 2 (n-1)^2 (n1)2的贡献,但是两者之间只能有一个产生贡献,2和 n − 1 n-1 n1可以产生 ( n − 2 ) 2 (n-2)^2 (n2)2的贡献,3和 n − 2 n-2 n2可以产生 ( n − 3 ) 2 (n-3)^2 (n3)2的贡献,依此类推。如果1不产生 ( n − 1 ) 2 (n-1)^2 (n1)2的贡献,那 n n n也不会产生 ( n − 1 ) 2 (n-1)^2 (n1)2的贡献,那么假设1和 n n n都产生 ( n − 2 ) 2 (n-2)^2 (n2)2的贡献,当 i ≥ 1 i \ge 1 i1时, i 2 > 2 ( i − 1 ) 2 i^2 >2 (i-1)^2 i2>2(i1)2,所以不如产生 ( n − 1 ) 2 (n-1)^2 (n1)2的贡献,综上,我们得到了一个代价的上界。我们考虑构造操作序列达到这个上界,那么可以先 ( 1 , n ) (1,n) (1,n)贡献 ( n − 1 ) 2 (n-1)^2 (n1)2,再 ( 2 , n ) (2,n) (2,n)贡献 ( n − 2 ) 2 (n-2)^2 (n2)2,再 ( n − 1 , 1 ) (n-1,1) (n1,1)贡献 ( n − 2 ) 2 (n-2)^2 (n2)2,再 ( 3 , n ) (3,n) (3,n)贡献 ( n − 3 ) 2 (n-3)^2 (n3)2……最后输出操作后的序列 p p p,逆向输出操作序列。

复杂度: O ( n ) O(n) O(n)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int t,n;
int p[maxn];
vector<pii>ans;
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			p[i]=i;
		}	
		ans.clear();
		ll cost=0;
		swap(p[1],p[n]);
		ans.pb(mp(1,n));
		cost+=1ll*(n-1)*(n-1);
		for(int i=2;i<=n/2;i++){
			swap(p[i],p[n]);
			ans.pb(mp(i,n));
			swap(p[n-i+1],p[1]);
			ans.pb(mp(n-i+1,1));
			cost+=2ll*(n-i)*(n-i);
		}
		if(n%2==1){
			swap(p[n/2+1],p[1]);
			ans.pb(mp(n/2+1,1));
			cost+=1ll*(n/2)*(n/2);
		}
		printf("%lld\n",cost);
		for(int i=1;i<=n;i++)printf("%d ",p[i]);
		puts("");
		printf("%d\n",sz(ans));
		for(int i=sz(ans)-1;i>=0;i--){
			printf("%d %d\n",ans[i].fi,ans[i].se);
		}
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值