Codeforces Round #713 (Div. 3)-E. Permutation by Sum-题解

Codeforces Round #713 (Div. 3)-E. Permutation by Sum

传送门
Time Limit: 2 seconds
Memory Limit: 256 megabytes

Problem Description

A permutation is a sequence of n n n integers from 1 1 1 to n n n, in which all the numbers occur exactly once. For example, [ 1 ] [1] [1], [ 3 , 5 , 2 , 1 , 4 ] [3, 5, 2, 1, 4] [3,5,2,1,4], [ 1 , 3 , 2 ] [1, 3, 2] [1,3,2] are permutations, and [ 2 , 3 , 2 ] [2, 3, 2] [2,3,2], [ 4 , 3 , 1 ] [4, 3, 1] [4,3,1], [ 0 ] [0] [0] are not.

Polycarp was given four integers n n n, l l l, r r r ( 1 ≤ l ≤ r ≤ n ) 1 \le l \le r \le n) 1lrn) and s s s ( 1 ≤ s ≤ n ( n + 1 ) 2 1 \le s \le \frac{n (n+1)}{2} 1s2n(n+1)) and asked to find a permutation p p p of numbers from 1 1 1 to n n n that satisfies the following condition:

For example, for n = 5 n=5 n=5, l = 3 l=3 l=3, r = 5 r=5 r=5, and s = 8 s=8 s=8, the following permutations are suitable (not all options are listed):

Help Polycarp, for the given n n n, l l l, r r r, and s s s, find a permutation of numbers from 1 1 1 to n n n that fits the condition above. If there are several suitable permutations, print any of them.

Input

The first line contains a single integer t t t ( 1 ≤ t ≤ 500 1 \le t \le 500 1t500). Then t t t test cases follow.

Each test case consist of one line with four integers n n n ( 1 ≤ n ≤ 500 1 \le n \le 500 1n500), l l l ( 1 ≤ l ≤ n 1 \le l \le n 1ln), r r r ( l ≤ r ≤ n l \le r \le n lrn), s s s ( 1 ≤ s ≤ n ( n + 1 ) 2 1 \le s \le \frac{n (n+1)}{2} 1s2n(n+1)).

It is guaranteed that the sum of n n n for all input data sets does not exceed 500 500 500.

Output

For each test case, output on a separate line:

If there are several suitable permutations, print any of them.

Sample Input

5
5 2 3 5
5 3 4 1
3 1 2 4
2 2 2 2
2 1 1 3

Sample Onput

1 2 3 4 5 
-1
1 3 2 
1 2 
-1

题目大意

给你 4 4 4个数 n 、 l 、 r 、 s n、l、r、s nlrs,让你找到一个 n n n的全排列,它满足从第 l l l个数累加到第 r r r个数的和等于 s s s


解题思路

n n n的全排列中,最大的数是 n n n,最小的数是 1 1 1,且各个数各不相同。
所以 l l l r r r r − l + 1 r-l+1 rl+1个数(记为 n u m num num)中,和的最小值是 m = 1 + 2 + ⋯ + n u m = ( 1 + n u m ) ∗ n u m / 2 m=1+2+\cdots+num=(1+num)*num/2 m=1+2++num=(1+num)num/2,和的最大值是 M = ( n − n u m + 1 ) + ( n − n u m + 2 ) + ⋯ + n = ( n − n u m + 1 + n ) ∗ n u m / 2 M=(n-num+1)+(n-num+2)+\cdots+n=(n-num+1+n)*num/2 M=(nnum+1)+(nnum+2)++n=(nnum+1+n)num/2
如果 s s s界于这两个数之间,就能找到符合条件的全排列,否则就不行。
如果可以找到,就把 l l l r r r中的数先初始值为最小的 1 1 1 n u m num num,再不断将这些数变大(变得尽量大),知道他们的和等于 s s s
变大过程是从最后一个数( n u m num num)开始变到尽可能大(不超过 n n n且和不超过 s s s),然后倒数第二个数( n u m − 1 num-1 num1)开始变到尽可能大(不超过 n − 1 n-1 n1且和不超过 s s s ⋯ \cdots 知道和等于 s s s


AC代码

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;
int a[505];
int getNext(map<int,bool>::iterator &it)//找到下一个输出的数(可以先看下面主函数)
{
    while(it->second==false)it++;//这个数出现过了,继续往下找
    return it->first;//找到返回
}
int main()
{
    int N;
    cin>>N;
    while(N--)//N组测试样例
    {
        int n,l,r,s;
        cin>>n>>l>>r>>s;
        int num=r-l+1;//从l到r共有r-l+1个数
        int m=(1+num)*num/2;//和最小为m
        int M=(n-num+1+n)*num/2;//和最大为M
        if(s<m||s>M)//如果s不介于他们之间
        {
            puts("-1");//不行
            continue;
        }
        for(int i=0;i<num;i++)//l到r初始值从1到num
        {
            a[i]=i+1;
        }
        int diff=s-m;//s比现在的这num个数大diff,还需要再加上diff
        int toM=n;//一个数最大变到n
        int loc=num-1;//目前处理的数的下标(从第r个数开始处理)
        while(diff)//差值不为0
        {
            int thisMaxDiff=toM-a[loc];//这个数改变所能引起的最大的差值
            int thisRealDiff=min(thisMaxDiff, diff);//真正变大的量(不能超过diff)
            diff-=thisRealDiff;//差距减小了thisRealDiff
            a[loc]+=thisRealDiff;//这个数变大了thisRealDiff
            toM=a[loc]-1;//一个数最大变到的数-1
            loc--;//下次处理前面一个数
        }
        map<int,bool>ma;//map储存一个数是否还能使用
        for(int i=1;i<=n;i++)
        {
            ma[i]=1;//初始值还可以使用
        }
        for(int i=0;i<num;i++)
        {
            ma[a[i]]=0;//从l到r中要出现的数不能再使用
        }
        map<int,bool>::iterator it=ma.begin();//从头开始找
        // for(map<int,bool>::iterator temp=ma.begin();temp!=ma.end();temp++)
        // {
        //     printf("%d %d\n",temp->first, temp->second);
        // }
        loc=0;
        for(int i=1;i<=n;i++)
        {
            if(i>=l&&i<=r)//是l到r中的一个数
            {
                printf("%d ",a[loc++]);//以及计算出,直接输出
            }
            else//否则
            {
                printf("%d ",getNext(it));//没出现过的数中找到下一个
                it++;
            }
        }
        puts("");//换行
    }
    return 0;
}

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/115599358

  • 15
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tisfy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值