(\)
(Description)
(N)个微博用户,按顺序给出(M)条系统记录,每个人只能看到好友的微博(看不见自己的):
- (! x) 表示用户(x)发了一条微博
- (+ x y) 表示用户(x)和用户(y)成为了好友
- (- x y) 表示用户(x)和用户(y)解除了好友关系
输入序列保证好友关系合法,即没有无效操作,问经过这(M)次操作这(N)个人分别看到了多少条微博。
- (Nin [1,2 imes10^5]),(Min [1,5 imes 10^5])
(\)
(Solution)
-
首先用(set)直接模拟的方法是能过的,对每一个人都开一个(set),当加入的时候就互相减掉各自前缀累计发微博的数量,当分开的时候就相互加上发微博的数量,实际上就是一种前缀和的形式。注意到此方法之所以需要开(set),是为了计算到最后也没有分开的那部分人之间的贡献。
-
考虑化简这个过程,将序列操作反向,同样的没遇到一个分开代表之后他们就认识了,所以减掉前缀,每遇到一个加入代表后面他们不认识了,所以加上前缀。这一方法不用开(set)维护的原因是,正序末尾需要考虑的部分,即只有加入而没有分开的部分这个方法考虑到了,而每个人必定要认识才能计数,就是说所有的区间开始的加号是一定有的,所以不需要担心少算的情况。
(\)
(Code)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define M 500010
#define N 200010
#define R register
#define gc getchar
using namespace std;
char c;
int n,m,cnt[N],opt[N];
struct tsk{int f,x,y;}s[M];
inline int rd(){
int x=0; bool f=0; c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
int main(){
n=rd(); m=rd();
for(R int i=1;i<=m;++i){
while(c!='!'&&c!='+'&&c!='-') c=gc();
if(c=='!'){s[i].f=2;s[i].x=rd();}
else{s[i].f=(c=='+');s[i].x=rd();s[i].y=rd();}
}
for(R int i=m;i;--i){
if(s[i].f==2) ++opt[s[i].x];
else if(s[i].f==1){
cnt[s[i].x]+=opt[s[i].y];
cnt[s[i].y]+=opt[s[i].x];
}
else{
cnt[s[i].x]-=opt[s[i].y];
cnt[s[i].y]-=opt[s[i].x];
}
}
for(R int i=1;i<=n;++i) printf("%d ",cnt[i]);
return 0;
}