CCPC2020Weihai H Message Bomb

Message Bomb

题目简述

m个人n个群s个操作,群一开始都是空的,操作有以下三种:

  1. 有一个人x加入了群y,保证x在操作前不在y中。
  2. 有一个人x退出了群y,保证x在操作前在y中。
  3. 人x在群y中发送了一条消息,此时在y中除了x的人会收到一个消息。

求:每个人收到的消息数目。

解题思路

记录每个操作时候每个群的总消息数,然后利用退出时间开始时间之间的群消息数差分计算收到的消息数。

也就是在加群的时候减去当前群里的消息总数,在退群的时候加上当前群里的消息总数

但是此时会存在两个问题:

  1. 自己发的消息也会被统计到差分里面。
  2. 有些人加入的群一直都没退过群。

针对第一个问题,因为自己发的每个消息,都会被统计到上述的差分中。所以,最后可以直接把差分计算的结果减去自己发送的消息总数,即可解决第一个问题。

针对第二个问题,只需要在最后时刻统计,每个人还在哪个群里即可。具体实现方法可以采用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;
}

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 点我我会动 设计师:白松林 返回首页