牛客练习赛113 E 小红走排列
题目意思是输出任意一个排列,使得所有相邻元素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);
//i需不需要减去k作出贡献,就k减去i-2后不会变成负数
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();
}