题目传送门
这道题是我灵光一闪突然想到的做法。
首先叙述一下题意:
这道题的意思就是说:给你四个数n,a,b,s让你构造一个长度为n的数字序列,这个序列的要求是数字必须是在1~n中的,而且不能有重复的数字,并且在下标a ~ b所对应的你构造的数组的和为s。
那么这道题就十分容易就能转换成为如下问题:
请问b-a+1个数字能否组成s?
那么我们设有要求的区段的数组所对应的初始值为(1,2,3……),也就是最小值,s-min所对应的就是有要求的区段的向右移动的位数。
例如:
3 1 2 4这一组样例,我们就是:
构造的数组的长度为3
我们先确定1~2区间应该填写哪些数
先填入最小一组数(从小到大遍历,防止出现重复的现象)
a[1]=2,a[2]=1(从大到小遍历数据,防止出现重复的情况)
a[1] 2->3
a[2] 1->2
同理如果n=4:
那么a[1] 2->3->4
a[2] 1->2->3
这样的话就可以最多向右移动两位
那么a[1]=2,可以向右移动3-[(2-1)+1]位,也就是可以向右移动1位,a[2]同理(防止出现相同的情况,所以只能最多移动到2).
每一个数都最多可以向右移动(n-(b-a+1))位,那么我们只需要找到s-n,然后从a[1]开始遍历,一直到达目标数组就可以了。
下面是AC代码
#include<iostream>
#include<map>
#include<cstring>
using namespace std;
typedef long long ll;
map<int,int>mp;
int an[210210];
int ans[210200];
int main(){int T;cin>>T;for(int i=1;i<=600;i++)an[i]=i;while(T--){memset(ans,0,sizeof ans);mp.clear();int n,a,b,s;cin>>n>>a>>b>>s;int num=b-a+1;int minn=0,maxx=0;for(int i=1;i<=num;i++){minn+=an[i];}for(int i=n-num+1;i<=n;i++){maxx+=an[i];}if(s<minn||s>maxx){printf("-1\n");continue;}int di=s-minn;int kk=n-num;for(int i=a,j=num;i<=b;i++,j--){int u=kk;ans[i]=j;if(di)for(int pp=1;pp<=kk;pp++){ans[i]++;di--;if(di==0)break;}mp[ans[i]]++;}for(int i=1;i<=n;i++){if(ans[i]==0){int uu=0;for(int i=1;i<=n;i++){if(!mp[i]){mp[i]=1;uu=i;break;}}ans[i]=uu;}}for(int i=1;i<=n;i++){printf("%d ",ans[i]);}printf("\n");}
}