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

来源:互联网 发布:长川软件 编辑:程序博客网 时间:2024/05/22 16:00

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都烂大街了,不写了,网上一搜一大把

原创粉丝点击