你考虑我们 A1 只需要通过自加凑一个最大的数,然后将所有的数都变成正数,最后做一次前缀和即可。(不懂可以看看落谷题解)
好,我们现在去看 Hard Version
的 \(31\) 次操作怎么分配:
-
前缀和(全为正)/ 后缀和 (全为负)—— \(19\) 次
-
还剩下 \(12\) 次,不知道该怎么做。
我们的目标便变为了:在 \(12\) 次之内让所有数变成全为正/全为负
现在,我们需要整理一下我们手中的方法:
- 我们
Easy Version
中是怎么做的呢,我们找了一个数,然后让它变成极大/极小,这一步在最劣情况下显然是 \(5\) 次 (\(1\to2\to4\to8\to16\to32\)),然后所有数都要变一遍 —— \(5+n\) - 当然,我们也有一种方法就是说找一个绝对值最大的数,然后让它给所有数加一遍,让其他的数和它符号相同 —— \(n\)
当然上面的 \(n\) 其实是上限,准确来说应该将 \(n\) 替换为和选出的数异号的数的个数。
那么,我们就可以将两种方法结合一下。
- 我们先找到一个绝对值最大的数 \(x\),然后计算和这个数符号相同的数的个数(包括 \(0\))
- 如果同号的数的个数 \(\geq 7\)(异号的数的个数 \(\leq 12\)),那么就直接让这个这些数都加上 \(x\)(\(\leq 12~times\))
- 否则,就造一个和 \(x\) 异号的绝对值极大的数,然后将 \(x\) 和与 \(x\) 同号的数都加上这个数。(\(\leq 5 + 6 + 1~times\))
最后将序列变为原序列的前/后缀和序列即可.
code:
#include<bits/stdc++.h>
using namespace std;
const int NN = 40;
int t,n;
int a[NN];
int maxn,pos;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
maxn = 0,pos = 0;
for(int i = 1; i <= n; ++i){
scanf("%d",&a[i]);
if(abs(a[i]) > maxn) maxn = abs(a[i]), pos = i;
}
if(maxn == 0){puts("0");continue;}
int cnt = 0;
for(int i = 1; i <= n; ++i){
if(a[i] * a[pos] >= 0 && i != pos) ++cnt;
}
if(cnt == n-1){
printf("%d\n",n-1);
}
else if(cnt < 7){
printf("%d\n",5 + cnt + n);
for(int i = 1; i <= n; ++i)
if(a[i] * a[pos] < 0){pos = i;break;}
for(int i = 1; i <= 5; ++i) printf("%d %d\n",pos,pos);
for(int i = 1; i <= n; ++i)
if(a[i] * a[pos] <= 0 && i != pos) printf("%d %d\n",i,pos);
}
else{
printf("%d\n",n - cnt - 1 + n - 1);
for(int i = 1; i <= n; ++i){
if(a[i] * a[pos] < 0 && i != pos) printf("%d %d\n",i,pos);
}
}
if(a[pos] > 0) for(int i = 1; i < n; ++i) printf("%d %d\n",i+1,i);
else for(int i = n; i > 1; --i) printf("%d %d\n",i-1,i);
}
}
标签:cnt,int,题解,Hard,pos,Version,maxn,printf
From: https://www.cnblogs.com/rickylin/p/17687788.html