CF1512E Permutation by Sum(思维)

题目传送门
这道题是我灵光一闪突然想到的做法。
首先叙述一下题意:
这道题的意思就是说:给你四个数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");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值