前言
(大雾 一群神仙构造拆式子。我乱搞想骗分的贪心没有WA就离谱。顺便吐槽一下题意好难懂。我做题得跟用来判SPJ的checker.cpp一起看就离谱。另外祝大家1月1日新年快乐。
题目大意
需要在一条笔直的路上建 n n n 个法阵,编号为 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n。
给定一个长度为 n n n 的数组 a a a,表示在 a 1 a_1 a1 到 a n a_n an 的位置建法阵,要给法阵编号。
需要来检测法阵效果,从 1 1 1 号法阵走到 2 2 2 号,从 2 2 2 号再走到 3 3 3 号,直到走到 n n n 号,再从 n n n 号走回 1 1 1 号。
由于法阵的特殊效果,从 i i i 个走到 i + 1 i+1 i+1 个的距离是 ∣ a i × p − a i + 1 × q ∣ \left|a_i\times p-a_{i+1}\times q\right| ∣ai×p−ai+1×q∣。特别的,从 n n n 号走回到 1 1 1 号的距离是 ∣ a n × p − a 1 × q ∣ \left|a_n\times p-a_1\times q\right| ∣an×p−a1×q∣。 p , q p,q p,q 是给定的两个常数, a i , a i + 1 a_i,a_{i+1} ai,ai+1 是两个法阵的位置。
来求一下最大的行走距离,并输出对应法阵从
1
1
1 号到
n
n
n 号排列方法。
对于
100
%
100\%
100% 的数据满足
10
≤
n
≤
1
0
6
10\le n\le 10^6
10≤n≤106,
1
≤
p
,
q
≤
1
0
5
1\le p,q \le 10^{5}
1≤p,q≤105,
1
≤
a
i
≤
1
0
5
1\le a_i\le 10^{5}
1≤ai≤105。
思路
暴力深搜 O ( n n ) \mathcal{O}(n^n) O(nn),肯定T飞了。考虑优化。由于出题人连大样例的构造方式都不给了,小样例又特别水,所以可以猜测本题应该不会十分困难。
感觉本题好像可以贪心的样子,也没啥其他的思路了,想想试试。如果要贪心,那我就按照这样构造试试:
- 找最大的放到新序列里
- 找最小的放到新序列里
- 从 a a a 中删除这两个数
听着很绕,其实就是按照一大一小一大一小···这样排序。测一下小样例,发现不对。然后发现上述情况没有考虑 p , q p,q p,q,感觉这是不是错的原因。之后发现,如果 q q q 更大,那么按照小大小大···这么排序更好,因为这可以让大数变得更大,就算大的很小也不亏。所以分类讨论。当 p > q p>q p>q 时按照前者进行运算,否则按照后者进行。
时间复杂度 O ( n log n ) O(n\log{n}) O(nlogn)。
代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll MAXN=1e6+5;
ll n,p,q,a[MAXN],b[MAXN];
bool cmp(ll A,ll B){
return A>B;
}
int main(){
cin>>n;
cin>>p>>q;
for (int i = 1; i <=n ; ++i) {
cin>>a[i];
}
if(p>q){
sort(a+1,a+n+1);
}else{
sort(a+1,a+n+1,cmp);
}
ll l=1,r=n;
ll sz=0;
while (l<=r){
b[++sz]=a[l];//放最小/大的
b[++sz]=a[r];//放最大/小的
l++;
r--;
}
ll ans=0;
for (int i = 1; i <n ; ++i) {
ans+= abs(b[i]*p-b[i+1]*q);
}
ans+= abs(b[n]*p-b[1]*q);
cout<<ans<<endl;
for (int i = 1; i <=n ; ++i) {
printf("%lld ",b[i]);
}
return 0;
}
总结
通过了分析题面和大胆猜测的出了贪心的做法。同时在经过错误后的分析得出了正解。