CCPC 威海 - H. Message Bomb —— 流动前缀和,妙

博客介绍了如何利用‘流动前缀和’的思想解决一个关于群聊消息接收数量的复杂问题。在给定人数、群组数和操作数的情况下,通过反转常规前缀和的思路,计算每个人接收到的消息数。关键在于考虑每个人加入群组后的消息贡献,而非每个消息对所有人的影响。代码中使用了集合来跟踪每个人的群组状态,并处理未退出群组的情况。最后,总结了实现此算法需要注意的细节。
摘要由CSDN通过智能技术生成
题目链接

题意:

给出 m 个人,n 个群组,s 个操作;
( 1 ≤ n ≤ 100000 , 1 ≤ m ≤ 200000 , 1 ≤ s ≤ 1000000 ) (1≤n≤100000,1≤m≤200000,1≤s≤1000000) (1n100000,1m200000,1s1000000)

每个操作有三种类型:

  • 1 x y:表示 人x 进入 群组y;
  • 2 x y:表示 人x 退出 群组y;
  • 3 x y:表示 人x 在 群组y 中发送了一条消息,群组y 中除了 x 的所有人都会收到。

问,最终每个人收到了多少条消息?


思路:

场上怎么想都不能做,模拟也不行,将集合中的所有人求前缀和也不行。。
归结于陷入了一个思想中:要把集合中的所有人的消息数+1 !

其实这题关键在于思维的转变,和之前做的一些不寻常的前缀和题目类似,我称之为 “流动前缀和”。

将每个群组看作一条延伸的线段,线段的长度随着消息数增长。
如果一个人在某个时刻 x 加入群组了,那么在时刻 y 退出群组后,其在这个群组中收到的消息数就为:时刻 y 时的总消息数 - 时刻 x 时的总消息数
这个时间差中的消息数就是这个群组对此人答案的贡献。

思路完全相反于之前的思路!
之前的思路是考虑每条消息求群中所有人的影响。
而现在是反过来考虑每个人加入一个群组所得的贡献。
太妙了!

此外有些细节要注意:
1、最后有人没有退出群组怎么办呢?
所以要开一个set记录每个人所在的群组,退出就消除该记录。最后遍历set中剩余的没有退出的人和群组。

2、因为每个人发的消息自己对自己没有贡献,所以最终每个人的贡献要减去自己发的消息数。


Code:
#include<bits/stdc++.h>
using namespace std;

const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N], ans[N], sum[N];
set<PII> st;

signed main(){
	Ios;
	int k;
	cin>>m>>n>>k;
	while(k--)
	{
		int op, x, y;
		cin>>op>>x>>y;
		if(op==1)
		{
			ans[x] -= sum[y];
			st.insert({x ,y});
		}
		else if(op == 2){
			ans[x] += sum[y];
			st.erase({x, y});
		}
		else{
			ans[x]--;
			sum[y]++;
		}
	}
	for(auto it:st)
	{
		int x = it.fi, y = it.se;
		ans[x] += sum[y];
	}
	
	for(int i=1;i<=n;i++) cout<<ans[i]<<"\n";
	
	return 0;
}

太妙了啊!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值