前缀和思想

本文解析了CF1132C Painting the Fence中的前缀和策略,并介绍了CF608B Hamming Distance Sum中利用位运算优化求解的方法。通过巧妙地利用前缀和和按位对比,解决两个实际编程问题的高效算法。
摘要由CSDN通过智能技术生成

这道题是一个十分巧妙的前缀和思想的题目
题目如下
题目传送门-----CF1132C Painting the Fence
墙长为n,q个工人,每个工人固定粉刷一个区间,区间可能重叠,现在要去掉两个工人,求剩下q-2个工人最多粉刷多少墙。(3≤n,q≤5000 ) (1 ≤ l i ≤ r i ≤n)。

那么对于这道题来说通过观察数据我们就知道需要用n^2算法,而又知道这个需要找到两个去除的小画家,所以说我们需要用到类似于冒泡排序的枚举思想。

pim数组存放的是我们去除i这个小画家以后,剩下的作画区间剩余数为1的个数的前缀和,(比方说i等于1的时候,我们需要去掉1这个小画家的所有作画区间(也就是c[l---->r]都减1),剩下的c[i]为1的数目),为什么我们需要统计这个呢,
举个栗子:
比方说 1 2 3 4 5分别作画次数是 0 2 2 2 4
两个小画家的作画区间分别是2 3 4 2 3 4 5
那么在删掉第一个小画家的时候这个区间的作画次数就变成了0 1 1 1 4
那么再枚举第二个小画家的时候我们只用看在他这个区间内,所有为1的点,在这第二个小画家删除之后都是不符合题目要求的,

ans=max(ans,sum-(pim[r[j]]-pim[l[j]-1]));

因此这一句代码的意思就是(sum是枚举了第一个小画家还剩下的有颜色的点减去第二个枚举的小画家区间内可以被删掉成为无颜色的点,就是我们这一次枚举的有颜色的点),题目中又说了需要我们找到最大的有颜色点,当然就需要每一次枚举的过程中取max。

#include<iostream>
using namespace std;
int main(){
	int n,m,ans=0;
	int l[1000010];
	int r[1000010];
	//分别记录每一个小画家的作画区间 
	int c[1000011];
	//这是每一个点被小画家的作画的次数记录 
	int pim[1000000];
	//前缀和数组 
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>l[i]>>r[i];
		for(int j=l[i];j<=r[i];j++){
			c[j]++;
		}
	}	
	for(int i=1;i<=m;i++){
		//枚举第一个点进行消除作画,这是第一次枚举

		int sum=0; 
		for(int j=l[i];j<=r[i];j++)
			c[j]--;
		for(int j=1;j<=n;j++){
			if(c[j]==1)pim[j]=pim[j-1]+1;
			else pim[j]=pim[j-1];
			if(c[j]>0)sum++;
		}
		for(int j=i+1;j<=m;j++){
		//这是第二次枚举
			ans=max(ans,sum-(pim[r[j]]-pim[l[j]-1]));
		}
		for(int j=l[i];j<=r[i];j++)c[j]++;
	}
	cout<<ans<<endl;
	return 0;
}

我太菜了!!!!!!!
下面一道题也是关于前缀和的很巧妙的题目
CF608B Hamming Distance Sum
题意:这道题其实就是按位对比,找到不相同的总数
举个例子:

位数1234
对比串01
标准串0011

01先和00比发现有一位不一样那么答案数目加一,然后0进入下一位,01和01做对比,这两个字符串完全相同,那么接下来0进入下一位,发现有一位不一样那么答案数接着加一。最后这个例子的答案就是2!
这道题首先暴力模拟会超时,那么我们就应该在题目中寻找规律,如何做呢?按位对比,还是上面的例子,对比串的1号位将会和标准串的123位对比,以此类推2号位的将会依次和标准串的234号位进行对比,那么我们只要找到这个区间上和我们这一位的对比串不相同的数字个数的前缀和就可以了,比如对于对比串的1号位来说:我们只需要找到123号上面所有为1的个数和,对于对比串的2位来说,至于找到234号上面的所有为0的位置个数和他们的总数就是我们想要得到的答案了!
那么以此类推,假设对比串长度为u,标准串的长度为l,现在遍历到了对比串的j位上面,那么我们只需要找到对应的对比串中j—>l-u+j的位置的前缀和就可以了,这样题目就变得很简单了,下面是答案代码。

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
ll s0[1010100];
ll s1[1010100];
int main(){
	char a[10101010];
	char b[10101010];
	cin>>a+1;
	cin>>b+1;
	for(ll i=1;b[i]!='\0';i++){
		int x1=0,x0=0;
		if(b[i]=='0') x0++;
		else x1++;
		s1[i]+=x1+s1[i-1];
		s0[i]+=x0+s0[i-1];
	}
	ll y=strlen(a+1);
	ll u=strlen(b+1);
	ll sum=0;
	for(int j=1;j<=y;j++){
		if(a[j]=='1'){
			sum+=s0[u-y+j]-s0[j-1];
		}else if(a[j]=='0'){
			sum+=s1[u-y+j]-s1[j-1];
		}
	}
	
		cout<<sum<<endl;
		return 0; 
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值