bzoj4419: [Shoi2013]发微博 (三种做法)

Description

刚开通的SH微博共有n个用户(1..n标号),在短短一个月的时间内,用户们活动频繁,共有m条按时间顺序的记录:
! x 表示用户x发了一条微博;
+ x y 表示用户x和用户y成为了好友
- x y 表示用户x和用户y解除了好友关系
当一个用户发微博的时候,所有他的好友(直接关系)都会看到他的消息。
假设最开始所有人之间都不是好友关系,记录也都是合法的(即+ x y时x和y一定不是好友,而- x y时x和y一定是好友)。
问这m条记录发生之后,每个用户分别看到了多少条消息。
Input

第1行2个整数n,m。
接下来m行,按时间顺序读入m条记录,每条记录的格式如题目所述,用空格隔开。
Output

输出一行n个用空格隔开的数(行末无空格),第i个数表示用户i最后看到了几条消息。
Sample Input

2 8

! 1

! 2

  • 1 2

! 1

! 2

  • 1 2

! 1

! 2
Sample Output

1 1

只有第4和第5条记录对应的消息被看到过。其他消息发送时,1和2不是好友。

对100%的数据,N<=200000,M<=500000

感想

这题我有三个方法..
我的是最垃圾的。。
别的都很好

题解

1 线段树(我的)

这个做法很蠢啊。。写了4000B。。
最近思维退化,只会用线段树了

我们可以吧+和,-分开弄
然后离线可以知道每个人按时间顺序依次影响到的是哪些人。。
然后按顺序可以弄成一段连续的区间。。
对于每个人维护一个当前扫到右边的坐标,然后每一次他发信息就整体加上就好了
不想写了

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int N=200005;
const int M=500005;
int n,m;
struct qq
{
    int c;//0:发微博     1:删除     2:加上 
    int x,y,id;
}s[M],s1[M],s2[M];//原来的操作   增加操作   删除操作 
bool cmp (qq a,qq b)
{
    return a.x==b.x?a.id<b.id:a.x<b.x;
}
int cnt=0,cnt1=0;
void ins ()
{
    scanf("%d%d",&n,&m);
    for (int u=1;u<=m;u++)
    {
        char ss[5];
        scanf("%s",ss);
        if (ss[0]=='!') {s[u].c=0;scanf("%d",&s[u].x);continue;}
        if (ss[0]=='-') 
        {
            s[u].c=1;
            scanf("%d%d",&s[u].x,&s[u].y);
            s1[++cnt]=s[u];s1[cnt].id=cnt;
            s1[++cnt]=s[u];s1[cnt].id=cnt;swap(s1[cnt].x,s1[cnt].y);
        }
        if (ss[0]=='+') 
        {
            s[u].c=2;
            scanf("%d%d",&s[u].x,&s[u].y);
            s2[++cnt1]=s[u];s2[cnt1].id=cnt1;
            s2[++cnt1]=s[u];s2[cnt1].id=cnt1;swap(s2[cnt1].x,s2[cnt1].y);
        }
    }
    //for (int u=1;u<=m;u++) printf("%d %d %d\n",s[u].x,s[u].y,s[u].c);
}
int L[N];//这个人增加的左端点在哪里 
int R[N];//这个人的右端点到了哪里
int L1[N];//这个人的删除左端点到了哪里
int R1[N];//这个人的删除左端点到了哪里
int now1=0,now2=0;//两个操作给到的编号
int a[M],b[M];
void prepare ()
{
    if (cnt!=0)
    {
        sort(s1+1,s1+1+cnt,cmp);

        for (int u=1;u<=cnt;u++)
        {
            if (s1[u].x!=s1[u-1].x)
            {
                L[s1[u].x]=u;
                R[s1[u].x]=u-1;
            }
            a[u]=s1[u].y;
            //printf("%d ",a[u]);
        }
    }
    if (cnt1!=0)
    {
        sort(s2+1,s2+1+cnt1,cmp);
        for (int u=1;u<=cnt1;u++)
        {
            if (s2[u].x!=s2[u-1].x)
            {
                L1[s2[u].x]=u;
                R1[s2[u].x]=u-1;
            }
            b[u]=s2[u].y;
        }
    }
}
struct qt
{
    int l,r;
    int s1,s2;
    int c;
}tr[M*2];int num=0;
void bt (int l,int r)
{
    int a=++num;
    tr[a].l=l;tr[a].r=r;
    tr[a].c=0;
    if (l==r) return ;
    int mid=(l+r)>>1;
    tr[a].s1=num+1;bt(l,mid);
    tr[a].s2=num+1;bt(mid+1,r);
}
void update (int now)
{
    int s1=tr[now].s1,s2=tr[now].s2;
    tr[s1].c+=tr[now].c;
    tr[s2].c+=tr[now].c;
    tr[now].c=0;
}
void change (int now,int l,int r)
{   
    //printf("%d %d\n",l,r);
    if (l>r||l==0) return ;
    if (tr[now].l==l&&tr[now].r==r)
    {
        tr[now].c++;
        return ;
    }
    update(now);
    int s1=tr[now].s1,s2=tr[now].s2;
    int mid=(tr[now].l+tr[now].r)>>1;
    if (r<=mid) change(s1,l,r);
    else if (l>mid) change(s2,l,r);
    else change(s1,l,mid),change(s2,mid+1,r);
}
int ans[N];
void dfs (int now,int f)
{
    if (tr[now].l==tr[now].r)
    {
        ans[a[tr[now].l]]=ans[a[tr[now].l]]+tr[now].c*f;
        return ;
    }
    update(now);
    dfs(tr[now].s1,f);dfs(tr[now].s2,f);
}
void dfs1 (int now,int f)
{
    if (tr[now].l==tr[now].r)
    {
        ans[b[tr[now].l]]=ans[b[tr[now].l]]+tr[now].c*f;
        return ;
    }
    update(now);
    dfs1(tr[now].s1,f);dfs1(tr[now].s2,f);
}
void solve ()
{
//0:发微博     1:删除     2:加上
    if (cnt>=1)
    {
        num=0;bt(1,cnt);
        for (int u=1;u<=m;u++)
        {
            if (s[u].c==2)//第一次我只弄减法
                continue;  
            //printf("%d %d %d %d %d\n",s[u].c,s[u].x,s[u].y,L[s[u].x],R[s[u].x]);
            if (s[u].c==0)
                change(1,L[s[u].x],R[s[u].x]);
            if (s[u].c==1)//减法
            {
                R[s[u].x]++; 
                R[s[u].y]++;
            }
        }
        dfs(1,-1);
    }
    /*for (int u=1;u<=n;u++) printf("%d ",ans[u]);
    printf("\n");*/

    if (cnt1>=1)
    {
        num=0;bt(1,cnt1);
        for (int u=1;u<=m;u++)
        {
            if (s[u].c==1)//第一次我只弄加
                continue; 
            if (s[u].c==0)//加法
                change(1,L1[s[u].x],R1[s[u].x]);
            if (s[u].c==2)
            {
                R1[s[u].x]++; 
                R1[s[u].y]++;
            }
        }
        dfs1(1,1);
    }

    for (int u=1;u<n;u++) printf("%d ",ans[u]);
    printf("%d\n",ans[n]);
}
int main()
{
    ins();
    prepare();
    solve();
    return 0;
}

2 O(n)的做法 CYS的

这个做法应该是最快的吗吧。。
我们就从后往前扫
对于每个人统计一个他当前发了多少个微博
然后当-的时候两个都减去互相当前的微博数
当+的时候两个都加上互相当前的微博数
就可以了

3 网上很普遍的做法 用set或者map都可以

wohenshuai的map写法
然后set都烂大街了,不写了,网上一搜一大把

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值