1 条题解
-
0
自动搬运
来自洛谷,原作者为

是青白呀
咕咕咕?咕咕咕!搬运于
2025-08-24 22:44:15,当前版本为作者最后更新于2023-02-01 19:18:26,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P8976 「DTOI-4」排列
update 2023/2/2 添加了一个例子便于理解
题目描述
给你一个偶数 和两个整数 ,请你构造一个长为 的排列 ,使得其满足 且 $\displaystyle\sum_{i = \frac{n}{2} + 1}^{n} p_i \geq b$。无解则输出 。
思路分析
要将长度为 的排列分为两部分,分别满足两个不小于某个值的条件,则在考虑其中一个条件时,一定要尽可能使另一个条件能被得到满足。
在本题中,在考虑区间 时,要使该区间和等于 ,才能使后半部分区间和大于等于 的条件尽可能被满足。于是考虑如何选取 个数使得其和为 。首先我们容易知道,可以使得和为 的条件为 $a \in \left[\frac{n(1+\frac{n}{2})}{4},\frac{n(\frac{n+1}{2}+n)}{4} \right]$,其中左右边界分别对应取 的所有数和取 的所有数的情况。考虑先选 ,此时 。考虑将已选择的数替换为更大的数。为了避免替换时发生重复,每一次将已选中的 中最大的数 替换为 ,则每替换一次, 的值增加 。则我们共需操作 次,并将下一个 替换为 的值。这样可以保证所选择的数不重复。最后再判断剩余数的和是否满足大于等于 即可。
为了便于理解,这里举一个例子。
1 8 23 13一开始我们选中的是这些数:
1 2 3 4此时 ,太小了,与目标 的差值为 。接下来进行上述替换操作。
1 2 3 8 1 2 7 8 1 6 7 8此时的 为 。到此,我们共操作了 次,即 次,每一次都让 的值增加了 。若再操作一次使 变成 , 的值就会超过 ,不满足我们的贪心策略,进而使后四个数不小于 的要求无法满足。所以我们将最后一个 替换为 ,使得 。代入数据发现,在这里我们应将 替换为 。则最后的序列为:
2 6 7 8 1 3 4 5恰好满足条件。
这里需要注意, 不意味着无解,在这种情况下直接判断 与 的关系即可。
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+5; int t; signed main(){ int t; read(t); while(t--){ int n,a,b; read(n),read(a),read(b); int sum=(1+n/2)*n/4; if(sum>=a){//a选择1~n/2 if((1+n)*n/2-sum<b)printf("-1\n"); else{ for(int i=1;i<=n;i++) printf("%d ",i); printf("\n"); } continue; } int movnum=(a-sum)/(n/2);//增加n/2的次数 if(movnum>n/2||(movnum==n/2&&(a-sum)%(n/2))){//总操作次数不能大于n/2 printf("-1\n"); continue; } bool vis[N]={};//标记哪些数属于前半部分 int suma=0; for(int i=1;i<(n/2)-movnum;i++) suma+=i,vis[i]=1; suma+=(n/2)-movnum+(a-sum)%(n/2); vis[(n/2)-movnum+(a-sum)%(n/2)]=1; for(int i=(n/2)-movnum+1;i<=n/2;i++) suma+=i+n/2,vis[i+n/2]=1; if((1+n)*n/2-suma<b)printf("-1\n"); else{ for(int i=1;i<=n;i++) if(vis[i])printf("%d ",i); for(int i=1;i<=n;i++) if(!vis[i])printf("%d ",i); printf("\n"); } } return 0; }
- 1
信息
- ID
- 7357
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者