USACO 2024 January Contest, BronzeProblem 3. Balancing Bacteria

1.本题例子

输入样例:
2
-1 3
输出样例:
6

使用去除细菌的农药,功率等级为 1,使用五次。然后使用添加细菌的农药,功率等级为 22,使用一次。

描述的时候是先后再前,但实际考虑应该为先前再后。因为农药的影响为以末尾开端的等差数列。

求 Farmer John 使用喷雾器的最少次数,使得每块草地都具有健康草的推荐细菌值,这里所描述的推荐细菌值为0.

如果我们从1~N,依次利用等差数列的性质,将所有数值计算为0。

5
1 3 -2 -7 5

将第一个数值1转换为0,我们可以看出来只需要转换一次,喷洒农药的浓度为5即可,浓度为5是由于它的位置决定的(从右到左第五个),那么它的值就成为了喷洒的次数,定义变量total为需要增减农药的次数,由于此次的喷洒会对之后的数值都会产生影响,这个影响我们可以进行统计,在对下一个值进行分析时,将影响加入,一起来分析,以此类推。

在考虑影响的时候,考虑之前的操作对本结点的影响:

13-2-75喷洒一次细菌
影响前03-2-75对第二个值的影响为-2
影响后01-2-75喷洒一次细菌
影响前01-2-75对第三个值的影响不仅考虑这次的喷洒,之前喷洒的影响也要考虑
影响后00-2-75对第三个值的影响为-3-2
影响前00-7-75喷洒7次农药
影响后00005对第四个值的影响为-4-3+14
影响前00005对最后的值的影响为-5-4+21
影响后000017喷洒细菌17次

但是这样会超时。。。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
long long a[N];
int n;
long long b[N];
long long cnt,effect;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		long long total = a[i]+b[i];
		if(total>0){
			for(int j=i+1;j<=n;j++){
				b[j]-= (j-i+1)*total;
			}
			cnt+=total;
		}
		else{
			for(int j=i+1;j<=n;j++){
				b[j]-=(j-i+1)*total;
			}
			cnt-=total;
		}
		/*for(int j=1;j<=n;j++){
			cout<<b[j]<<' ';
		}
		cout<<endl;
		for(int j=1;j<=n;j++){
			cout<<a[j]+b[j]<<' ';
		}
		cout<<endl<<endl;*/
	}
	cout<<cnt;
	return 0;
}

2.程序优化

超时说明O(n2)要降到O(n),从每次操作对后续的影响入手,发现每次影响的值为之前的操作次数,

5

1 3 -2 -7 5

第一个值的操作对后续的影响是线性的:2,3,4,5,我们也可以写成这样的形式:

13-2-75
喷洒一次细菌的影响0+11+11+1+1=2+11+1+1+1=3+11+1+1+1+1=4+1

我们可以看出来喷洒后的影响效果为1,而随着数组位置的增加,增加1的个数增多,如果我们将影响的结果存储起来,后面的数都增加这个影响就可以啦,我们定义cur_ops为根据当前的数值决定喷洒的次数,cnt_ops为之前总共需要影响的值,contribution为总共需要影响的值,ans为总共喷洒的次数。

        contribution += cnt_ops;//在计算i位置的影响时,需要加上之前的影响
        a[i] += contribution;       //作用于i位置的影响生效
        ll cur_ops = -a[i];           //根据影响后的值计算当前需要多少次喷洒
        ans += abs(cur_ops);   //统计喷洒次数
        cnt_ops += cur_ops;    //对后续结点的影响增加了
        contribution += cur_ops; //将当前的喷洒影响增加到总影响当中,相当于1+1+。。。+1中的最后一个1.

#include <bits/stdc++.h>
using namespace std;

using ll = long long;

int main() {
	ios::sync_with_stdio(false); cin.tie(0);
	
	int n; cin >> n;
	vector<ll> a(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	
	ll ans = 0;
	ll contribution = 0;
	ll cnt_ops = 0;
	for (int i = 0; i < n; i++) {
		contribution += cnt_ops;
		a[i] += contribution;
		
		ll cur_ops = -a[i];
		ans += abs(cur_ops);
		cnt_ops += cur_ops;
		contribution += cur_ops;
	}
	
	cout << ans << '\n';
}

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值