网络上称一对情侣秀恩爱为“撒狗粮”,因为单身人士统称为“单身狗”。
在一个大型聚会上,所有宾客被安排坐在一张长条宴会桌边。如果一对情侣坐在一起,那么他们两人身边的单身狗就会被撒一脸狗粮;如果他们没有坐在一起,那么所有被夹在他们两人之间的单身狗都会被撒一脸狗粮。
本题就请你找出被撒狗粮最多(以“脸”为单位)的那位单身人士。
输入格式:
输入第一行给出一个正整数 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;
}