一、题意
构造一个长度为 n n n 的序列 P P P , 使 P P P 的前半段的数之和 ≥ a \geq a ≥a , 后半段的数之和 ≥ b \geq b ≥b 。
二、分析
1 ∼ n \sim n ∼n 每个数只能用一次,所以整个序列之和是 $\dfrac{n(n + 1)}{2} $(为方便,以下统称 S 1 S1 S1 ) 半个序列之和最大是 n 2 ( n 2 + 1 + n ) 2 \dfrac{\dfrac{n}{2}\left(\dfrac{n}{2} + 1 + n\right)}{2} 22n(2n+1+n) (为方便,以下统称 S 2 S2 S2 ),如果 a + b > S 1 a + b > S1 a+b>S1 或 a > S 2 a > S2 a>S2 或 b > S 2 b > S2 b>S2 ,就说明无解。
构造时我们只看前半个序列 (为方便,以下统称 A A A )。
一开始,我们将 A A A 置为 1 ∼ n 2 1 \sim \dfrac{n}{2} 1∼2n ,和为 n 2 ( n 2 + 1 ) 2 \dfrac{\dfrac{n}{2}\left(\dfrac{n}{2} + 1\right)}{2} 22n(2n+1) (为方便,以下统称 S 3 S3 S3 ),如果 S 3 ≥ a S3 \geq a S3≥a ,那么就输出 1 ∼ n 1 \sim n 1∼n 。
否则令 T = a − S 3 T = a - S3 T=a−S3 ,$ X = T \div \dfrac{n}{2}$ ,$ F = T % \dfrac{n}{2}$ 。
下面来解释一下,若不够就得补上,每个数最多可以增大 n 2 \dfrac{n}{2} 2n ,我们可以将 A A A 的后 X X X 个数( n 2 \dfrac{n}{2} 2n , n 2 − 1 \dfrac{n}{2} - 1 2n−1 , n 2 − 2 \dfrac{n}{2} - 2 2n−2 , … \dots … )换成 P P P 的后 X X X 个数( n n n , n − 1 n - 1 n−1 , n − 2 n - 2 n−2 , … \dots … ),此时还会少 F F F ,我们可以将 A A A 的第 n 2 − X \dfrac{n}{2} - X 2n−X 个数换成 P P P 的第 n 2 − X + F \dfrac{n}{2} - X + F 2n−X+F 个数(这一步在 X ≠ 0 X \ne 0 X=0 的情况下才需要进行)。
这样序列 A A A 就构造完成了,输出 A A A ,然后将没用过的数输出即可。
三、代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,n,a,b;
int main()
{
cin>>t;
while(t--)
{
cin>>n>>a>>b;
if(n*(n+1)/2<a+b||n*(n+1)/2-(n/2)*(n/2+1)/2<a||n*(n+1)/2-(n/2)*(n/2+1)/2<b)//判断是否无解
{
cout<<-1<<endl;
continue;
}
ll t=(n/2)*(n/2+1)/2;//相当于上面的T
if(t<a)
{
ll t1=(a-t)/(n/2),x=(a-t)-t1*(n/2);//相当于上面的X、F
for(ll i=1;i<=n/2-t1-(bool)(x);i++)cout<<i<<" ";
if(x)cout<<x+n/2-t1<<" ";
for(ll i=n-t1+1;i<=n;i++)cout<<i<<" ";
//构造A
for(ll i=n/2-t1;i<=n-t1;i++)
{
if(i==x+n/2-t1)continue;
cout<<i<<" ";
}
//将没用过的数输出
cout<<endl;
}
else
{
for(ll i=0;i<n;i++)cout<<i+1<<" ";
cout<<endl;
}
}
return 0;
}