P3998 [SHOI2013] 发微博
题目翻译:
题目描述已经较为详细,这就不翻译了。
思路:
考虑暴力: 我们可以给每个人都添加一个关系链,每发出一次一条消息,就将所有与他有关系的答案依次加一。这样就统计出来了。但是这样的复杂度为 \(O(mn)\) 无法过。
考虑优化: 我们发现 \(m\) 次询问时难以优化掉的。考虑如何把 \(n\) 优化掉。我们发现暴力时每一次都要给所有人加一,那有什么办法可以不用每次都修改了?可以考虑前缀和。我们发现某一段有关系的区间内所加的消息数是等于,它那个人结束时发出的所有消息数,减去刚建立关系时的消息数,那我们只需要维护每个人发出消息数,在有人加入时,和退出时,分别减去和加上即可。
注意: 由于不一定所有人最后都会取消关系,所以我们最后要强制取消所有关系,那我们可以用 \(set\) 维护所有有关系的人,由于 \(set\) 操作都是 \(O(\log n)\) 的,所以最终复杂度为 \(O(m \log n)\)
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,cnt[N],ans[N];
set<int>s[N];
set<int>::iterator it;
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<=m;++i){char opt;cin>>opt;if(opt=='!'){int x;cin>>x;cnt[x]++;}if(opt=='+'){int x,y;cin>>x>>y;ans[x]-=cnt[y];ans[y]-=cnt[x];s[x].insert(y);s[y].insert(x);}if(opt=='-'){int x,y;cin>>x>>y;ans[x]+=cnt[y];ans[y]+=cnt[x];s[x].erase(y);s[y].erase(x);}}for(int i=1;i<=n;++i){for(it=s[i].begin();it!=s[i].end();++it){ans[i]+=cnt[*it];}}for(int i=1;i<=n;++i)cout<<ans[i]<<" ";return 0;
}