zoukankan      html  css  js  c++  java
  • luogu P3998 [SHOI2013]发微博

    题目描述

    刚开通的 SH 微博共有n个用户(1Ln标号),在这短短一个月的时间内,

    用户们活动频繁,共有m 条按时间顺序的记录:

    ! x 表示用户x发了一条微博;
    + x y 表示用户x和用户y成为了好友
    − x y 表示用户x和用户y解除了好友关系

    当一个用户发微博的时候,所有他的好友(直接关系)都会看到他的消息。

    假设最开始所有人之间都不是好友关系,记录也都是合法的(即+ x y时x和

    y一定不是好友,而− x y时x和y一定是好友)。

    问这 m 条记录发生之后,每个用户分别看到了多少条消息。


    输入输出格式

    输入格式:

    第 1行 2个整数n,m。

    接下来m 行,按时间顺序读入m 条记录,每条记录的格式如题目所述,用

    空格隔开。

    输出格式:

    输出一行 n 个用空格隔开的数(行末无空格),第i 个数表示用户i 最后看到

    了几条消息。


    输入输出样例

    输入样例#1: 
    2 8
    ! 1
    ! 2
    + 1 2
    ! 1
    ! 2
    - 1 2
    ! 1
    ! 2
    输出样例#1: 
    1 1

    说明

    n<=200000

    m<=500000


    30分?
    暴力模拟。
    我们记录在当前x点现在可以产生$cnt_x$的贡献
    对于每一次加边,记录它所到达的点,并记录当前这个点出现贡献次数为dis
    然后单次删除到x的边
    就是找到这个边,答案就会产生$cnt_x-dis$的贡献
    这样单次查找到这个点的复杂度是$O(n)$,结合m次的操作
    大概就能过30%
    100分
    对于上面的进行一个优化。
    对于每一个x点所到的点都可以看成一个集合,这样上面的做法是暴力查找集合中所有的元素。
    显然这一步可以用数据结构暴力搞,比如set,map,使查找维持在logn,但我们不用这样
    我们只需要集合中元素单调,就可以二分找到。
    于是每一次加点就找到一个满足的位置,使加入这个点,集合中元素依旧单调。
    删除就在vector中删,剩下的自动补充。
    每一个点开一个vector维护dis和所到达的位置
    直接在线搞出来就可以

    #include<iostream>
    #include<algorithm>
    #include<cstdio> 
    #include<map> 
    #include<vector>
    using namespace std;
    const int maxn=200000+233;
    vector< pair<int,int> >v[maxn];
    vector< pair<int,int> >::iterator it;
    int ans[maxn];
    int n,m;
    char ch;
    int cnt[maxn];
    int x,y;
    int main(){
        ios::sync_with_stdio(0);
        cin>>n>>m;
        for(;m;m--){
            cin>>ch;
            if(ch=='!'){
                cin>>x;
                cnt[x]++;
            }
            else{
                cin>>x>>y;
                if(ch=='+'){
                    v[x].insert(lower_bound(v[x].begin(),v[x].end(),make_pair(y,-2333)),make_pair(y,cnt[y]));
                    v[y].insert(lower_bound(v[y].begin(),v[y].end(),make_pair(x,-2333)),make_pair(x,cnt[x]));
                }
                else{
                    it=lower_bound(v[x].begin(),v[x].end(),make_pair(y,-2333));
                    ans[x]+=cnt[y]-(it->second);
                    v[x].erase(it);
                    it=lower_bound(v[y].begin(),v[y].end(),make_pair(x,-2333));
                    ans[y]+=cnt[x]-(it->second);
                    v[y].erase(it);
                }
            }
        }
        for(int i=1;i<=n;i++){
        for(it=v[i].begin();it!=v[i].end();++it)
        ans[i]+=cnt[it->first]-(it->second); 
        cout<<ans[i]<<" ";
        }
        return 0;
    }
    发微博

    但是其实可以离线从后往前减,这样做是O(m)的
    ~~应该没有人会在这里卡常数的~~
    by:s_a_b_e_r


    考虑一个用户x发微博的时候,他会对所有x当前的好友y产生一点答案贡献
    于是从xy成为好友,一直到xy解除好友关系,x对y产生的总贡献一共是(解除好友关系时x的微博数-成为好友时x的微博数)
    那么我们记录一个cnt[x],表示到目前为止x共发了多少条微博
    对于每个点建立一个set记录x的当前好友集合
    每次加(减)点的同时,把ans[x]减去(加上)cnt[y](有点像差分?)
    但是因为只有在解除好友的时候贡献才会被完整统计,所以最后要手动解除所有人的好友关系,即遍历一遍每个人的set
    时间复杂度O(mlogn),具体实现见代码

    #include<iostream>
    #include<cstdio>
    #include<set>
    using namespace std;
    const int N=200009;
    int n,m,cnt[N],ans[N];
    set<int>s[N];
    set<int>::iterator it;
    int main(){
        ios::sync_with_stdio(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;
    }
    微博(mlogn)

    做完之后一想,总觉得这个set大材小用了,能不能不用啊
    于是……开始考虑这个set到底起了什么作用
    归根结底就是因为最后需要手动解除一遍所有人的好友关系,才需要用set来维护每个人的好友集合,这样可以知道每个人剩下的好友都是谁
    那么能不能让他们到最后所有人都没有好友关系呢?
    反着处理所有操作就可以啦,因为一开始所有人都没有好友关系

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=200009,M=500009;
    int n,m,data[M][2],cnt[N],ans[N];
    char opt[M];
    int main(){
        ios::sync_with_stdio(0);
        cin>>n>>m;
        for(int i=1;i<=m;++i){
            cin>>opt[i];
            if(opt[i]=='!')cin>>data[i][0];
            if(opt[i]=='+')cin>>data[i][0]>>data[i][1];
            if(opt[i]=='-')cin>>data[i][0]>>data[i][1];
        }
        for(int i=m;i;--i){
            if(opt[i]=='!')++cnt[data[i][0]];
            if(opt[i]=='+'){
                ans[data[i][0]]+=cnt[data[i][1]];ans[data[i][1]]+=cnt[data[i][0]];
            }
            if(opt[i]=='-'){
                ans[data[i][0]]-=cnt[data[i][1]];ans[data[i][1]]-=cnt[data[i][0]];
            }
        }
        for(int i=1;i<=n;++i)cout<<ans[i]<<" ";
        return 0;
    }
    微博(m)

    这样只用O(m)就解决了问题 虽然mlogn就能过

    by:wypx



    s: 我永远喜欢珂朵莉

    w:珂教兴国x

  • 相关阅读:
    【Python 开发】第三篇:python 实用小工具
    【Python 开发】第二篇 :Python安装
    【Linux 运维】 安装PHP工具Composer
    【shell 每日一练6】初始化安装Mysql并修改密码
    【zabbix 监控】第三章 创建主机组和主机
    【Docker】第一篇 Docker的初始化安装部署
    【shell 练习5】编写简单的多级菜单
    【zabbix 监控】第二章 安装测试被监控主机
    【zabbix 监控】第一章 zabbix的安装配置
    1数组和字符串题解
  • 原文地址:https://www.cnblogs.com/ck666/p/8039488.html
Copyright © 2011-2022 走看看