[ SHOI 2013 ] 发微博
\(\\\)
\(Description\)
\(N\)个微博用户,按顺序给出\(M\)条系统记录,每个人只能看到好友的微博(看不见自己的):
- \(!\ x\) 表示用户\(x\)发了一条微博
- \(+\ x\ y\) 表示用户\(x\)和用户\(y\)成为了好友
- \(-\ x\ y\) 表示用户\(x\)和用户\(y\)解除了好友关系
输入序列保证好友关系合法,即没有无效操作,问经过这\(M\)次操作这\(N\)个人分别看到了多少条微博。
- \(N\in [1,2\times10^5]\),\(M\in [1,5\times 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;
}