原题大意
题目描述
一种很抽象的游戏。
轩轩和凯凯正在玩一款叫《龙虎斗》的游戏,游戏的棋盘是一条线段,线段上有 n 个兵营(自左至右编号 1∼n),相邻编号的兵营之间相隔 1 厘米,即棋盘为长度为 n−1 厘米的线段。i 号兵营里有 c[i] 位工兵。 下面图 1 为 n=6 的示例:
轩轩在左侧,代表“龙”;凯凯在右侧,代表“虎”。 他们以 m 号兵营作为分界, 靠左的工兵属于龙势力,靠右的工兵属于虎势力,而第 m 号兵营中的工兵很纠结,他们不属于任何一方。
一个兵营的气势为:该兵营中的工兵数 ╳ 该兵营到 m 号兵营的距离;参与游戏 一方的势力定义为:属于这一方所有兵营的气势之和。
下面图 2 为 n=6,m=4 的示例,其中红色为龙方,黄色为虎方:
游戏过程中,某一刻天降神兵,共有 s1 位工兵突然出现在了 p1 号兵营。作为轩轩和凯凯的朋友,你知道如果龙虎双方气势差距太悬殊,轩轩和凯凯就不愿意继续玩下去了。为了让游戏继续,你需要选择一个兵营 p2,并将你手里的 s2 位工兵全部派往 p2,使得双方气势差距尽可能小。
注意:你手中的工兵落在哪个兵营,就和该兵营中其他工兵有相同的势力归属(如果落在 m 号兵营,则不属于任何势力)。
输入格式
输入文件的第一行包含一个正整数 n,代表兵营的数量。
接下来的一行包含 n 个正整数,相邻两数之间以一个空格分隔,第 i 个正整数代 表编号为 i 的兵营中起始时的工兵数量 c[i] 。
接下来的一行包含四个正整数,相邻两数间以一个空格分隔,分别代表 m,p1,s1,s2。
输出格式
输出文件有一行,包含一个正整数,即 p2,表示你选择的兵营编号。如果存在多个编号同时满足最优,取最小的编号。
输入输出样例
输入样例1 输出样例1
6 2
2 3 2 3 2 3
4 6 5 2
| | | |
m p1 s1 s2
注:m是分界线,有s1个人突然出现在兵营p1,现要让s2个人出现在兵营p2,找出兵营p2。
一个兵营的气势为:该兵营中的工兵数 ╳ 该兵营到 m 号兵营的距离
双方以 m=4 号兵营分界,有 s1=5 位工兵突然出现在 p1=6 号兵营。 龙方的气势为:
2×(4−1)+3×(4−2)+2×(4−3)=14
虎方的气势为:
2×(5−4)+(3+5)×(6−4)=18
当你将手中的 s2=2 位工兵派往 p2=2 号兵营时,龙方的气势变为:
14+2×(4−2)=18
此时双方气势相等。
输入样例2 输入样例2
6 1
1 1 1 1 1 16
5 4 1 1
| | | |
m p1 s1 s2
注:m是分界线,有s1个人突然出现在兵营p1,现要让s2个人出现在兵营p2,找出兵营p2。
一个兵营的气势为:该兵营中的工兵数 ╳ 该兵营到 m 号兵营的距离
双方以 m=5 号兵营分界,有s1=1 位工兵突然出现在 p1=4 号兵营。
龙方的气势为:
1×(5−1)+1×(5−2)+1×(5−3)+(1+1)×(5−4)=11
虎方的气势为:
16×(6−5)=16
当你将手中的 s2=1 位工兵派往 p2=1 号兵营时,龙方的气势变为:
11+1×(5−1)=1511+1×(5−1)=15
此时可以使双方气势的差距最小。
数据规模与约定
1<m<n,1≤p1≤n。
对于 20% 的数据,n=3,m=2,c[i]=1,s1,s2≤100。
另有 20% 的数据,n≤10,p1=m,c[i]=1,s1,s2≤100。
对于 60% 的数据,n≤100,c[i]=1,s1,s2≤100。
对于 80% 的数据,n≤100,c[i],s1,s2≤100。
对于 100% 的数据,n≤10⁵,c[i],s1,s2≤10⁹。
---------------------------------------------------------------------------------------------------------------------------------
本题解法
因为本题n最大为10⁵,而龙虎斗是线性的,可以用一维数组存储,故选择采用暴力枚举,时间复杂度是O(n),绰绰有余。
60分解法
不难发现,前60分 c[i] 都为1,n也比较小,可以先入手。
我们可以先计算出龙方和虎方分别的势气总和,然后比较大小,确定是为哪一方招兵买马(并枚举出在哪一兵营加士兵能使两方势气差值最小),或者谁也不帮(即当龙方势气等于虎方势气时,p2等于m)。
因为 c[i] 都等于 1,所以循环时不用再乘上当前兵营的人数了。
所以,代码如下:
#include<bits/stdc++.h>
using namespace std;
int read(){//快读
int dat=0,t=1;
char ch;
ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')t=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
dat=(dat<<1)+(dat<<3)+(ch&15);
ch=getchar();
}
return dat*t;
}
int a[100004],b[100004];//a数组记录当前兵营的人数,b数组记录当前兵营距离分界线的距离
int n,i,m,s1,p1,s2,p2;
int sum_long,sum_hu;//sum_long表示龙方势气总和,sum_hu表示虎方势气总和
int min1=1000000001;//用min1来记录两方势气总和的最小差值
int main(){
n=read();
for(i=1;i<=n;i++){//输入每个兵营人数
a[i]=read();
}
m=read();
for(i=1;i<=n;i++){//预处理b数组
b[i]=abs(m-i);
}
p1=read();
s1=read();
s2=read();
for(i=1;i<m;i++){//求龙方势气总和
sum_long+=b[i];
}
for(i=m+1;i<=n;i++){//求虎方势气总和
sum_hu+=b[i];
}
if(p1<m){//加上新来的士兵势气
sum_long+=(m-p1)*s1;
}
if(p1>m){
sum_hu+=(p1-m)*s1;
}
if(sum_long>sum_hu){//虎方势气小,在虎方加士兵
for(i=m+1;i<=n;i++){//枚举p2
if(abs(sum_hu+s2*b[i]-sum_long)<min1){//比较在此兵营加士兵是否使两方差值目前最小
min1=abs(sum_hu+s2*b[i]-sum_long);//打擂台
p2=i;
}
}
}
if(sum_long<sum_hu){//龙方势气小,在龙方加士兵
for(i=1;i<m;i++){
if(abs(sum_long+s2*b[i]-sum_hu)<min1){
min1=abs(sum_long+s2*b[i]-sum_hu);
p2=i;
}
}
}
if(sum_long==sum_hu){//两方势气相等,在分界线加士兵
p2=m;
}
printf("%d",p2);//输出p2
return 0;
}
正解
思路和60分相似,但是我们又多了一些细节处理。
每次枚举还要考虑人数。
当然,我们可以直接把新来的s1为士兵加到兵营p1里去,假装他们本来就在p1兵营里,方便计算。
但是n最大为10⁵,c[i] 最大为10⁹,10⁵ * 10⁹ 显然超出了int范围,所以不开long long 见祖宗!
#include<bits/stdc++.h>
using namespace std;
long long read(){//快读
long long dat=0,t=1;
char ch;
ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')t=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
dat=(dat<<1)+(dat<<3)+(ch&15);
ch=getchar();
}
return dat*t;
}
long long a[100004],b[100004];//a数组记录当前兵营的人数,b数组记录当前兵营的势气
long long l,r;//l表示龙方势气总和,r表示虎方势气总和
long long min1;//用min1来记录两方势气总和的最小差值
long long n,i,m,s1,s2,p1,p2,k;//不开long long 见祖宗!
int main(){
n=read();
for(i=1;i<=n;i++){//输入每个兵营人数
a[i]=read();
}
m=read();
p1=read();
s1=read();
s2=read();
a[p1]+=s1;//加入新来的兵
p2=m;
for(i=1;i<=n;i++){
b[i]=abs(m-i)*a[i];//计算出每个兵营的势气
if(i<m)l+=b[i];//求龙方势气总和
else r+=b[i]; //求虎方势气总和
}
min1=abs(l-r);
if(l<r){//龙方势气小,在龙方加士兵
for(i=1;i<=m;i++){//枚举p2
k=s2*(abs(m-i));//计算在此处加士兵增加的势气
if((abs(l+k-r))<min1){//比较在此兵营加士兵是否使两方差值目前最小
min1=abs(l+k-r);//打擂台
p2=i;
}
}
}else if(r<l){//虎方势气小,在虎方加士兵
for(i=m+1;i<=n;i++){
k=s2*(abs(m-i));
if((abs(l-r-k))<min1){
min1=abs(l-r-k);
p2=i;
}
}
}
printf("%lld",p2);//输出p2
return 0;
}