传送门:1244G
标签:构造
题目大意
贝拉托夫队由2n名选手组成,他们被分为两个跑道;每个跑道上有n名选手。每个跑道上的选手编号从1到n。编号为i的选手需要i秒跑完整条跑道。比赛按以下方式进行:首先,两个跑道上的选手同时起跑;当速度慢的人跑到终点后,第二个选手开始跑,所有人都等待直到速度慢的人完成跑步,以此类推,直到所有n对选手都跑完全程。组织者希望比赛尽可能长,但如果持续时间超过k秒,观众就会感到无聊。作为团队教练,你可以选择每个跑道上的选手排列顺序(但不能更改每个跑道上的选手数量或交换不同跑道之间的选手)。
你需要选择每个跑道上的选手顺序,以便比赛的持续时间尽可能长,但不超过k秒。正式地说,你需要找到两个排列p和q(均包含n个元素),使得sum = max(p_i,q_i)的最大可能值不超过k。如果没有这样的对,请说明这一点。
输入:第一行包含两个整数n和k(1≤n≤106,1≤k≤n2)——每个跑道上的选手数量和比赛的最大可能持续时间。
输出:如果无法重新安排选手以确保比赛持续时间不超过k秒,则输出-1。否则,输出三行。第一行应包含一个整数sum——比赛的最长可能持续时间不超过k。第二行应包含一个排列p_1,p_2,…,p_n(1≤p_i≤n,所有p_i应互异)——第一个跑道上参加比赛的选手编号。第三行应包含一个排列q_1,q_2,…,q_n(1≤q_i≤n,所有q_i应互异)——第二个跑道上参加比赛的选手编号。sum = max(p_i,q_i)的值应该是最大可能的,但不应超过k。如果有多个答案,请输出任何一种。
算法分析
- 首先我们要理解哪些sum的值是可以获得的。显然,sum的最小可能值是mn = (n(n + 1)) / 2。sum的最大可能值是mx = ([n/2] + 1 + n) * [n/2] + n%2 * [n/2]。我们可以获得介于mn和mx之间的一切可能的sum值,我们将展示如何做到这一点。如果k < mn,则答案为-1(这是唯一这样的情况)。否则,答案存在,我们需要以某种方式构造它。
- 假设第一个排列是同构排列(1, 2, …, n),并且我们只改变第二个排列。最初,第二个排列也是同构的。现在我们有sum=mn,我们需要将其改为sum=k或至多可能的数字不超过k。为了做到这一点,让我们学习如何增加sum。让我们看看如果交换n和n - 1会发生什么。那么sum的值将增加1。如果我们交换n和n - 2,那么sum的值将增加2,依此类推。如果我们交换n和1,那么sum的值将增加n - 1。
- 所以,具体的算法实现如下:我们携带当前可以更改的排列段[l; r](一定存在,因为在某些交换之后,一些左端和右端的元素不能再增加sum的答案,因为它们已经被放置得最优了)以及我们要添加到sum中的值add,以获得最大的可能sum不超过k。初始l = 1, r = n, add = k - mn。现在让我们了解可以增加sum的最大值。现在它是r - l。如果这个值大于add,那么让我们交换pr和pr-add,打破循环(p是第二个排列)。否则,让我们交换pl和pr,减少add为r - l,并设置l := l + 1, r := r - 1。如果在某一时刻l变得大于或等于r,则打破循环。现在我们得到了第二个排列p,其sum的值最大且不超过k,我们可以计算数值答案(或打印min(mx, k)), 输出同构排列和排列p。
代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k;
const int maxn = 1e6+10;
int a[maxn],b[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
long long nm = (n+1)*n/2;
if(k<nm) {cout<<-1; return 0;}
long long ans=nm;
for(int i=1;i<=n;i++) a[i]=i;
int L=1,R=n;
while(L<=R && ans<k) {
long long add = a[R]-a[L];
while(L<=R && ans+add>k) {
L++;
add = a[R]-a[L];
}
swap(a[L],a[R]);
ans += add;
L++; R--;
}
cout<<ans<<'\n';
for(int i=1;i<=n;i++) cout<<i<<" ";cout<<'\n';
for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<'\n';
return 0;
}