[ 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;
}
posted @ 2018-09-17 21:57  SGCollin  阅读(141)  评论(0编辑  收藏  举报