Codeforces 1198B Welfare State:巧妙降低时间复杂度

Codeforces1198B高效解题策略
本文详细解析了Codeforces1198B题目的解题思路,通过巧妙利用操作顺序和更新市民最低收入的方式,将时间复杂度优化至O(n)。文章分享了zpf大佬的解题技巧,包括如何使用ops和opsmoney数组来记录操作顺序和财富值,从而避免重复计算,提高解题效率。

题目连接:

http://codeforces.com/problemset/status

题目大意:

题目大意为有n个市民,每个市民有ai点数财富,以下有q次操作,操作类型为两类,1类:把第p个市民的财富改为x,2类:把所有财富小于x的市民的财富变为x。

解题思路:

暴力模拟会超时(时间复杂度为n^2),这时候就需要用到一点点小技巧来降低时间复杂度。仔细观察题目会发现,操作1只是对某个市民的财产进行了修改,而操作二是对符合要求的所有市民的财产都进行修改。我们可以先把所有的操作2进行完,将结果保存,然后和需要进行操作1的市民所需要修改的值进行比较。如果所修改的值大于操作2给定的值(市民最低收入)操作1的修改正常进行,如果所修改的值大于操作2给定的值,本该进行的操作1失效,市民的财产不发生改变。这里还有一个小技巧,是在进行所有的操作2时,从后往前进行遍历,寻找最大值,此时的值为市民的最低收入。然后确定了市民最终的财富情况之后,打印输出即可。(总体时间复杂度为O(n))

以下思路来自zfp大佬:观察到在对第i个市民进行最后一次1类操作之后,将不会影响此后的所有第2类操作,也就是说如果此后的第2类操作的x > ai的财富,将可以修改第i个市民的财富,因此每个市民的最终财富 = 对该市民的最后一次第1类操作和此后的第2类操作 两者之间的最大值即可。用ops[]数组记录第1类操作的顺序,opsmoney记录第2类操作的财富,q次操作从后往前遍历,做opsmoney[ i ] = max (opsmoney[ i ] ,opsmoney[ i +1 ])的更新,记录第i次操作之后 x 的最大值,方便后续比较。

C++代码实现(源代码来自zpf,注释来自本人)

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
long long int ops[200005];
		//用来记录操作1的顺序 
long long int opsmoney[200005];
		//用来记录操作2的财富,即记录每次操作2所对应的公民最低财富值 
long long int money[200005];
		//用来记录每个公民的实际财富 
int main()
{
	int n;
	cin>>n;
	for(int i = 0;i<n;i++){
		cin>>money[i];
	}
	long long int q;
	cin>>q;
	memset(opsmoney,0,sizeof(opsmoney));
	memset(ops,0,sizeof(ops));
	for(int i = 0;i<q;i++){
		int flag;
		cin>>flag;
		if(flag == 1){
			long long int p;
			long long int x;
			cin>>p;
			cin>>x;
			ops[p-1] = i;
			money[p-1] = x; 
		}
	    if(flag == 2){
			long long int x;
			cin>>x;
			opsmoney[i] = x;
		}
	}
	for(int i = q-1;i>=0;i--){
		opsmoney[i] = max(opsmoney[i],opsmoney[i+1]);
		//寻找所有操作2中公民最低财富的最大值 
	}
	for(int i = 0;i<n;i++){
		money[i] = max(money[i],opsmoney[ops[i]]); 
		/*每个市民的最终财富 = 对该市民的最后一次操作1 和 此后的操作2中的最大值 
		
		两者之间的最大值*/
	}
	for(int i = 0;i<n;i++){
		if(!i){
			cout<<money[i];
		}
		else{
			cout<<" "<<money[i];
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值