题解 P5016 【龙虎斗 】
noip考完了,祝大家取的一个好成绩,期中考rp++;
这道题目读懂题意后就非常简单了:
给出n个数,再给出一个$m$位置,定义$a$和$b$,($m$不属于$a$或$b$):$a$为$1 \sim m-1$个数每个数用这个数到$m$的距离乘这个数的积之和,$b$则为$m+1 \sim n$中再在这$n$个每个数用这个数到$m$的距离乘这个数的积之和,然后在其中一个位置($p1$)加入了一个数($s1$),现在再给你一个数($s2$),求这个数加在哪一个位置能让$a$和$b$的差最小(加上去这个数的计算方法和之前相同)
那么根据题意我们可以总结出这道题的思路了
注 :此时的$a$和$b$我们已经加上了$s1$$(a||b)$,abs为绝对值
- 先把$s1$加到$p1$位置上,然后根据题意计算$a$和$b$的值
定义一个minn,用于更新最小差值,在定义一个ans用来更新能产生最小差值的位置
-
因为题目中说$s2$可以放在$m$上,而$m$上的值不影响$a$和$b$值,所以想让差值最小,可以先考虑一下最小差值为$abs(a-b)$的情况,所以先把$minn=abs(a-b)$,$ans=m$;
-
因为题目的中“势力”的定义,我们并不能得出a<b则最小差值的位置就在a区间内的结论,这个数到m点的距离会影响“势力”的值。所以只能暴力枚举从1~n的每一个位置
我们拿枚举$1 \sim m-1($即a区间)内的位置举例:
for(int i=1;i<m;i++)
{
if(abs(s2*(m-i)+a-b)<minn){
minn=abs(s2*(m-i)+a-b);
ans=i;
}
}
s2(m-i)+a即当s2的值插入到i位置时a的值
这样这道题目我们就已经可以代码实现出来了
CODE:
#include<iostream>
#include<cstdio>
#include<cctype>
#include<cmath>
using namespace std;
long long a[100010],s1,s2,sum2,sum1,minn;
int m,n,p1,ans;
inline int read()
{
int X=0;bool sign=0; char ch=0;
while(ch<'0'||ch>'9') {sign|=(ch=='-');ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<3)+(X<<1)+(ch^48);ch=getchar();}
return sign?-X:X;
}
inline void print(int x)
{
if(x<0)
putchar('-'),x=-x;
if(x>9)
print(x/10);
putchar(x%10+'0');
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
m=read();p1=read();s1=read();s2=read();
a[p1]+=s1;
for(int i=1;i<m;i++) //见解题思路第1点
sum1+=a[i]*(m-i);
for(int i=m+1;i<=n;i++)
sum2+=a[i]*(i-m);
minn=abs(sum1-sum2);ans=m;//见解题思路第2点
for(int i=1;i<m;i++) //见解题思路第3点
if(abs(sum1+s2*(m-i)-sum2)<minn) {
minn=abs(sum1+s2*(m-i)-sum2);
ans=i;
}
for(int i=m+1;i<=n;i++)
if(abs(sum2+s2*(i-m)-sum1)<minn){
minn=abs(sum2+s2*(i-m)-sum1);
ans=i;
}
print(ans);
}
再说些有关自己,有关这道题的事吧,这道题考场上花了近一个小时做了出来,就在要删掉freopen的注释时电脑异常关机,然后我第二题的代码就是第三题的了。申诉延时也因为认为是我个人原因而被拒绝,辛辛苦苦准备了一年的成果也就自然付之东流。我写这篇题解,也是为了证明我即使没有那道题的分数,也是有实力做出那道题的。
@洪以濠 @哲学濠 感谢你们对我的关心和开导。感谢洛谷给oier们提供了这么优秀的平台:D