Message Bomb
题目简述
m个人n个群s个操作,群一开始都是空的,操作有以下三种:
- 有一个人x加入了群y,保证x在操作前不在y中。
- 有一个人x退出了群y,保证x在操作前在y中。
- 人x在群y中发送了一条消息,此时在y中除了x的人会收到一个消息。
求:每个人收到的消息数目。
解题思路
记录每个操作时候每个群的总消息数,然后利用退出时间和开始时间之间的群消息数差分计算收到的消息数。
也就是在加群的时候减去当前群里的消息总数,在退群的时候加上当前群里的消息总数。
但是此时会存在两个问题:
- 自己发的消息也会被统计到差分里面。
- 有些人加入的群一直都没退过群。
针对第一个问题,因为自己发的每个消息,都会被统计到上述的差分中。所以,最后可以直接把差分计算的结果减去自己发送的消息总数,即可解决第一个问题。
针对第二个问题,只需要在最后时刻统计,每个人还在哪个群里即可。具体实现方法可以采用std::set的方法,也可以采用利用记录所有加退群记录:
- 针对其中每个加群记录,把最终时刻的群消息加到贡献里,相当于退群了。
- 针对其中每个退群记录,把最终时刻的群消息在贡献里减去,抵消之前的加群操作。
这样操作完成之后,如果在最后这个群已经被退了,那么正负相抵。如果这个群没被退,那么就相当于退了一次群。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+17, M = 2e5+17, S = 1e6+17;
int n, m, s, current[N], message[M], sendmessage[M];
int fr[M], nxt[S], to[S], type[S], tails;
void add(int f, int t, int ty){
nxt[++tails] = fr[f];
fr[f] = tails;
to[tails] = t;
type[tails] = ty;
}
int main(){
scanf("%d %d %d", &n, &m, &s);
for(int i=1, opt, x, y;i<=s;++i){
scanf("%d %d %d",&opt, &x, &y);
switch(opt){
case 1:
add(x, y, 1);
message[x] -= current[y];
break;
case 2:
add(x, y, 2);
message[x] += current[y];
break;
case 3:
current[y] ++;
sendmessage[x] ++;
break;
}
}
for(int i=1;i<=m;++i){
for(int p=fr[i]; p; p=nxt[p]){
if(type[p] == 1)
message[i] += current[to[p]];
else
message[i] -= current[to[p]];
}
}
for(int i=1;i<=m;++i){
printf("%d\n", message[i] - sendmessage[i]);
}
return 0;
}