L1-7 撒狗粮(一分钟讲清差分)

网络上称一对情侣秀恩爱为“撒狗粮”,因为单身人士统称为“单身狗”。
在一个大型聚会上,所有宾客被安排坐在一张长条宴会桌边。如果一对情侣坐在一起,那么他们两人身边的单身狗就会被撒一脸狗粮;如果他们没有坐在一起,那么所有被夹在他们两人之间的单身狗都会被撒一脸狗粮。
本题就请你找出被撒狗粮最多(以“脸”为单位)的那位单身人士。

输入格式:

输入第一行给出一个正整数 N(≤ 50 000),是已知情侣的对数;随后 N 行,每行给出一对情侣——为方便起见,每人对应一个 ID 号,为 5 位数字(从 00000 到 99999),ID 间以空格分隔;之后给出一个正整数 M(≤ 80 000),为参加派对的总人数;随后一行按座位从左到右的顺序给出这 M 位客人的 ID,以空格分隔。题目保证无人脚踩两条船。

输出格式:

在一行中输出被撒狗粮最多的单身人士。如果不唯一,按 ID 递增顺序列出。ID 间用 1 个空格分隔,行的首尾不得有多余空格。
题目保证至少有一个输出。

输入样例:

4
11111 22222
33333 44444
55555 66666
77777 88888
10
11111 33333 88888 22222 23333 55555 66666 10000 44444 12345

输出样例:

10000 23333 88888

注意:88888 虽然有伴侣,但在聚会上是单身。

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

栈限制

8192 KB

这道题目,主要考点在于在两个情侣夹着人这种情况下,不能在循环里面套循环,否则会超时,由于这个是针对数组的某个范围内的所有元素进行相同的操作,所以我们想到可以使用差分来做。什么是差分呢?? 那就借这个题目简单介绍一下

首先我们要理解差分,就要知道什么是前缀和,前缀和,说白了就是Sn求和,就是前n个数求和(s[i] = s[i - 1] + a[i])。

比如这里我们有一个数组,里面元素为 1 2 3 4 5 6

那么他的前缀和数组就是 1 3 6 10 15 21 

而差分是什么呢??

差分就是我们刚才求前缀和过程的逆过程,也就是如果我们的原数组是1 3 6 10 15 21

那么他的差分数组就是 1 2 3 4 5 6

具体怎么求得呢??

就是 a[i] = s[i] - s[i - 1]  (前缀和数组是s,当i 等于0的时候那么a[i]j = s[i] )

那么差分数组能做什么呢??

用一个例子来说明一下:

假如a[1] += 2,  那么相应的s[1] == 5  s[2] = 8  s[3] = 12  s[4] = 17  s[5] = 23

现在我们来观察一下这个数组,你有没有发现什么规律呢,对了!那就是从1这个位置之后的所有前缀和数组都加了相同的数字2  为什么会这样呢??
其实我们从前缀和数组的创建过程就可以找到原因,因为每一个前缀和数组的元素都是a数组的前缀和,就比如:

s[1] = a[0] + a[1]

s[2] = a[0] + a[1[ + a[2] 

s[3[ = a[0] + a[1[ + a[2] + a[3]

那么如果你改变了a[1]的值,由于在s数组中pos = 1 这个位置以及后面的所有元素都需要加上一个a[1],所以a[1]改变了多少,这些个元素就要改变多少。
所以!是不是非常神奇,我只需要改变一个数组元素的值,我就能改变另外一个数组中成千上万个值!!

举一个差分经常被用来做的事情:

在某个范围内对这个范围内的所有元素做相同的操作,就比如我要在[x,y]这个范围内让这个范围内的所有元素都加上2

那么我们先求这个数组的差分数组c

然后令 c[x] + 2, c[y + 1] - 2 ,接着再重新更新原数组,s[i] = s[i - 1] + c[i] 就能实现这个过程。

那么有的同学就要要问了,说:"竹子,竹子,我直接让原数组遍历一下,然后让这个范围内的元素全部加2不是更简单吗”  
但是同学你要知道,如果我的这个操作要进行n多次呢,那你遍历的话每次都要遍历原数组一遍,但是如果用差分数组的话就只是两个元素的事,等到所有范围加减操作进行完,我们再遍历更新数组,这样的话时间复杂度能大大减少,从另外一个角度来看,差分数组实际上在做一个“标记”的事情。如果你读到这里,恭喜你已经初步掌握了差分!!!

#include "bits/stdc++.h"
using namespace std;
const int N = 1e6 + 2;
int c[N]; //差分数组
int p[N]; //对象
int vis[N]; //单身数组 
int id[N];//记录宴会上的人的坐标 
int name[N]; //姓名数组 
int done[N]; //标记数组 
int main(){
	memset(p, -1, sizeof(p));
	memset(id, -1, sizeof(id));
	int n;
	cin>>n;
	int a, b;
	while(n--){
		cin>>a>>b;
		p[a] = b;
		p[b] = a;
	} //分配对象 
	int m;
	cin>>m; 
	for(int i = 1; i <= m; i ++){
		cin>>a;
		name[i] = a;
		id[a] = i;
	} //输入对应位置的宾客名称  以及记录该宾客在哪个位置 
	for(int i = 1; i <= m; i ++){
		if(p[name[i]] == -1 || id[p[name[i]]] == -1) vis[i] = 1; 
	} //看满足题目要求的单身汉是谁   一是没对象 二是有对象但是不在宴会上 
	for(int i = 1; i <= m; i ++){
		if(vis[i]) continue; //如果这是个单身汉,不处理 
		if(done[i]) continue; //如果这个情侣我们已经处理过,不处理 
		int x = i, y = id[p[name[i]]]; //找到一对情侣所在位置 
		done[y] = 1; //标记该情侣中的后面的 那个人的位置,以防止后面再次遇到他 
		if(y - x == 1) { //如果是相邻的   
			c[x - 1] ++;
			c[x] --;
			c[y + 1] ++;
			c[y + 2] --;
		}//如果是夹着人的 
		else if(y - x > 1){
			c[x + 1] ++;
			c[y] --;
		}		
	}
	int ans = -1;
	vector<int> v;
	for(int i = 1; i <= m; i ++){ //差分转原数组(可爱的,喜欢的) 
		c[i] += c[i - 1];
		if(!vis[i]) continue;//如果他是情侣,直接让他滚 
		if( c[i] > ans){ //如果不是,直接找到被撒狗粮最多的人 
			ans = c[i];
			v.clear();
			v.push_back(name[i]);
		}
		else if(c[i] == ans){
			v.push_back(name[i]);
		}
	}
	sort(v.begin(), v.end());
	for(int i = 0; i < v.size(); i ++){
		printf("%05d",v[i]); //切记格式化输出,这个不给你说是格式错误,直接答案错误 
		if(i != v.size() - 1) cout<<" ";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小竹子14

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值