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