PS:如果读过题了可以跳过题目描述直接到题解部分
题目
题目描述
刚开通的 SH 微博共有 n 个用户(1∼n 标号),在这短短一个月的时间内,用户们活动频繁,共有 m 条按时间顺序的记录:
! x 表示用户 x 发了一条微博;
+ x y 表示用户 x 和用户 y 成为了好友
− x y 表示用户 x 和用户 y 解除了好友关系
当一个用户发微博的时候,所有他的好友(直接关系)都会看到他的消息。
假设最开始所有人之间都不是好友关系,记录也都是合法的(即 + x y
时 x 和 y 一定不是好友,而 − x y
时 x 和 y 一定是好友)。
问这 m 条记录发生之后,每个用户分别看到了多少条消息。
输入格式
第 1 行两个整数 n, m。
接下来 m 行,按时间顺序读入 m 条记录,每条记录的格式如题目所述,用空格隔开。
输出格式
输出一行 n 个用空格隔开的数(行末无空格),第 i 个数表示用户 i 最后看到了几条消息。
样例
输入
2 8 ! 1 ! 2 + 1 2 ! 1 ! 2 - 1 2 ! 1 ! 2
输出
1 1
说明/提示
对于 100% 的数据,n≤200000, m≤500000
题解
这道题用暴力超时不是显然的嘛……
所以可以用一个所谓类似差分的办法。
只要在加好友的时候减去前面已经发过的消息,再在删好友的时候把发过的消息全部加起来,最后抵消得到的就是这个人对另一个的影响。
事实上,我们会发现他们一开始加了好友但最后不一定会删,所以我们可以考虑把过程倒过来,毕竟只要加了好友的,他们一定会加好友(废话
对,就是这样的,要是看不懂可以感性理解看代码理解一下。
代码实现
//洛谷 P3998 [SHOI2013]发微博
#include<iostream>
#include<cstdio>
#include<bitset>
using namespace std;
int n,m;
int b[500010];
int x[500010][2];
int cnt[200010];
int c[200010];
void in(int &x){
int nt;
x=0;
while(!isdigit(nt=getchar()));
x=nt^'0';
while(isdigit(nt=getchar())){
x=(x<<3)+(x<<1)+(nt^'0');
}
}
int main(){
register int i;
in(n);
in(m);
for(i=1;i<=m;++i){
while((b[i]=getchar())==10);
if(b[i]==33){
in(x[i][0]);
}
else if(b[i]==43){
in(x[i][0]);
in(x[i][1]);
}
else{
in(x[i][0]);
in(x[i][1]);
}
}
for(i=m;i>=1;--i){
if(b[i]==33){
++cnt[x[i][0]];
}
else if(b[i]==43){
c[x[i][0]]+=cnt[x[i][1]];
c[x[i][1]]+=cnt[x[i][0]];
}
else{
c[x[i][0]]-=cnt[x[i][1]];
c[x[i][1]]-=cnt[x[i][0]];
}
}
for(i=1;i<n;++i){
printf("%d ",c[i]);
}
printf("%d",c[n]);
return 0;
}