牛客练习赛113 E 小红走排列

发布时间 2023-07-09 17:24:36作者: 突破铁皮

题目意思是输出任意一个排列,使得所有相邻元素i到i-1的距离之和为k

首先k最小为n-1,即当n为1~n的规则排列时,我们先减去一个n-1,然后根据多出来的k来对元素进行重排列

为了方便考虑,我们对于每次移动i时,只考虑i-1和i之间的多出来的距离变换来抵消k,对于i+1来说如果无需移动则维持和i的距离与之前一致

构造一个双端队列来构造序列,一开始只有1,沿着右方向走,方向为右放入队尾,否则放入队头,方向改变对距离的变换如下所示

例如 123如果4方向改变则变为4123,4与3的距离由1变为了3,我们发现增加了2,实际就是增加了n-2

根据这个规律,我们先预处理一遍k,从n-2到1开始,如果k>=i的话,则说明i+2方向改变对于i+1的影响为距离增加i,因此k就减去一个i

由此每次都是把i添加到队列的两头则和i-1之间的距离要么是1要么是i-1

#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <set>
#include <utility>
#include <vector>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N=1e5+10;
bool p[N];
deque<int> dq;
int n;
ll k;
void solve(){
	cin>>n>>k;
	k-=n-1;//得到多余的距离
	for(int i=n-2;i>=1;i--)
	if(k>=i) k-=i,p[i]=1;//如果增加的距离大于等于i的话说明i+2方向需要改变
	int f=1;
	dq.push_back(1);
	for(int i=2;i<=n;i++){
		if(p[i-2]) f=1-f;
		if(f) dq.push_back(i);
		else dq.push_front(i);
	}
	while(!dq.empty()){
		cout<<dq.back()<<" ";
		dq.pop_back();
	}
    
}
int main(){
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
}