P3998 [SHOI2013]发微博 方法记录
[SHOI2013]发微博
题目描述
刚开通的 SH 微博共有 \(n\) 个用户(\(1\sim n\) 标号),在这短短一个月的时间内,用户们活动频繁,共有 \(m\) 条按时间顺序的记录:
! x 表示用户 x 发了一条微博;
+ x y 表示用户 x 和用户 y 成为了好友
− x y 表示用户 x 和用户 y 解除了好友关系
当一个用户发微博的时候,所有他的好友(直接关系)都会看到他的消息。
假设最开始所有人之间都不是好友关系,记录也都是合法的(即 + x y
时 \(x\) 和 \(y\) 一定不是好友,而 − x y
时 \(x\) 和 \(y\) 一定是好友)。
问这 \(m\) 条记录发生之后,每个用户分别看到了多少条消息。
输入格式
第 \(1\) 行两个整数 \(n\), \(m\)。
接下来 \(m\) 行,按时间顺序读入 \(m\) 条记录,每条记录的格式如题目所述,用空格隔开。
输出格式
输出一行 \(n\) 个用空格隔开的数(行末无空格),第 \(i\) 个数表示用户 \(i\) 最后看到了几条消息。
样例 #1
样例输入 #1
2 8
! 1
! 2
+ 1 2
! 1
! 2
- 1 2
! 1
! 2
样例输出 #1
1 1
提示
对于 \(100\%\) 的数据,\(n\leq 200000\), \(m\leq 500000\)
\(50pts\)
最好想的一种做法。
开一个逻辑型二维数组\(frd[N][N]\)来储存好友关系,若\(x\)加\(y\)为好友,则令\(frd[x][y]=1,frd[y][x]=1\)。反之,若解除\(x\)与\(y\)之间的好友关系,则令\(frd[x][y]=0,frd[y][x]=0\)。
当\(x\)发微博时,遍历\(i~n\),若\(frd[x][i]==1\),则说明\(x\)与\(i\)存在好友关系,\(i\)看到的微博数\(+1\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
const int N=15005;
int n,m,att[N];
bool frd[N][N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
char op[2];
scanf("%s",op);
if(op[0]=='!')
{
scanf("%d",&x);
for(int i=1;i<=n;i++)
if(frd[x][i])
att[i]++;
}
else if(op[0]=='+')
{
scanf("%d%d",&x,&y);
frd[x][y]=1;
frd[y][x]=1;
}
else if(op[0]=='-')
{
scanf("%d%d",&x,&y);
frd[x][y]=0;
frd[y][x]=0;
}
}
for(int i=1;i<=n;i++)
{
if(i==n) printf("%d",att[i]);
else printf("%d ",att[i]);
}
return 0;
}
缺点很明显,首先因为使用了二维数组,导致空间不够开;其次时间复杂度\(O(n*m)\),丑。
\(100pts\)
\(O(nlogn)\)
吸取了\(50pts\)算法中空间不够开的教训,我们考虑使用STL,声明 \(set<int> s[200005]\)成功开出想要的数量,\(set[x]\)用于储存\(x\)的朋友圈。
(对set不熟悉的同学看看这篇)
接下来考虑如何给答案作出贡献。
举个具象化的例子。在甲乙交友之前,甲统共发布了3篇微博,乙统共发布了5篇微博;甲乙分手时,甲统共发布了6篇微博,乙统共发布了7篇微博。那么,甲看到乙发的微博为\(7-5=2(篇)\),乙看到甲发的微博为\(6-3=3(篇)\)。由此可推出结论:
从甲乙成为好友,一直到甲乙分手,甲对乙产生的总贡献一共是
分手时甲的微博数-成为好友时甲的微博数
那么我们就可以用一个数组\(cnt[x]\)来储存目前\(x\)发布的微博数,这个处理无法离线。
对于添加好友:两人各自的微博浏览数减去彼此的目前微博数
对于解除好友:两人各自的微博浏览数加上彼此的目前微博数
由此可见,在解除好友时才会对答案作出贡献。所以最后需要把余下的好友关系进行名义上的解除,以统计截至最后一个时刻的答案。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
const int N=200005;
int n,m;
int cnt[N],att[N];
set<int> s[N];
set<int>::iterator it;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
char op[2];
int x,y;
scanf("%s",op);
if(op[0]=='!')
{
scanf("%d",&x);
cnt[x]++;
}
else if(op[0]=='+')
{
scanf("%d%d",&x,&y);
att[x]-=cnt[y];
att[y]-=cnt[x];
s[x].insert(y);
s[y].insert(x);
}
else if(op[0]=='-')
{
scanf("%d%d",&x,&y);
att[x]+=cnt[y];
att[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++)
att[i]+=cnt[*it];
for(int i=1;i<=n;i++)
{
if(i==n) printf("%d",att[i]);
else printf("%d ",att[i]);
}
return 0;
}
\(100pts\)
\(O(n)\)
我们可不可以不用\(set\)或是其它二维结构?
我们需要\(set\)来做什么?需要用它来记录每个人的朋友圈。
\(set\)实际上做了什么?可以看到,在第一遍处理输入时,\(set\)的工作只有插入和擦去,真正发挥的作用只有在最后时刻解除好友关系。
为什么需要用\(set\)来解除好友关系?因为最后时刻有余下的好友。
有没有办法使最后没有余下的好友?有,我们可以记录每次操作再倒序处理,这时最后状态就变成了最初状态:即没有任何好友关系的状态。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
const int N=200005;
const int M=500005;
int n,m,att[N],cnt[N];
int frd[M][2];
char op[M][2];
int main()
{
freopen("sina.in","r",stdin);
freopen("sina.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%s",op[i]);
if(op[i][0]=='!')
{
scanf("%d",&frd[i][0]);
}
else if(op[i][0]=='+')
{
scanf("%d%d",&frd[i][0],&frd[i][1]);
}
else if(op[i][0]=='-')
{
scanf("%d%d",&frd[i][0],&frd[i][1]);
}
}
for(int i=m;i>=1;i--)
{
if(op[i][0]=='!')
{
cnt[frd[i][0]]++;
}
else if(op[i][0]=='+')
{
att[frd[i][0]]+=cnt[frd[i][1]];
att[frd[i][1]]+=cnt[frd[i][0]];
}
else if(op[i][0]=='-')
{
att[frd[i][0]]-=cnt[frd[i][1]];
att[frd[i][1]]-=cnt[frd[i][0]];
}
}
for(int i=1;i<=n;i++)
{
if(i==n) printf("%d",att[i]);
else printf("%d ",att[i]);
}
return 0;
}
P3998 [SHOI2013]发微博 方法记录的更多相关文章
- 洛谷 P3998 [SHOI2013]发微博
洛谷 P3998 [SHOI2013]发微博 洛谷传送门 题目描述 刚开通的 SH 微博共有n个用户(1Ln标号),在这短短一个月的时间内, 用户们活动频繁,共有m 条按时间顺序的记录: ! x 表示 ...
- [BZOJ 4419][Shoi2013]发微博
4419: [Shoi2013]发微博 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 665 Solved: 364[Submit][Status] ...
- BZOJ 4419: [Shoi2013]发微博 set模拟
4419: [Shoi2013]发微博 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4419 Description 刚开通的SH微博共 ...
- 【BZOJ4419】[SHOI2013]发微博(???)
[BZOJ4419][SHOI2013]发微博(???) 题面 BZOJ 洛谷 题解 一道\(easy\),每个点维护一下要给周围的点加上多上,如果额外连了一个点进来就给他把标记减掉,如果删掉了一条边 ...
- BZOJ4419: [Shoi2013]发微博 暴力
Description 刚开通的SH微博共有n个用户(1..n标号),在短短一个月的时间内,用户们活动频繁,共有m条按时间顺序的记录: ! x 表示用户x发了一条微博: + x y 表示用户x和用 ...
- BZOJ4419:[SHOI2013]发微博(乱搞)
Description 刚开通的SH微博共有n个用户(1..n标号),在短短一个月的时间内,用户们活动频繁,共有m条按时间顺序的记录: ! x 表示用户x发了一条微博: + x y 表示用户x和用 ...
- [SHOI2013]发微博
Description 刚开通的SH微博共有n个用户(1..n标号),在短短一个月的时间内,用户们活动频繁,共有m条按时间顺序的记录: ! x 表示用户x发了一条微博: + x y 表示用户x ...
- 【bzoj4419】[Shoi2013]发微博 STL-set
题目描述 刚开通的SH微博共有n个用户(1..n标号),在短短一个月的时间内,用户们活动频繁,共有m条按时间顺序的记录: ! x 表示用户x发了一条微博: + x y 表示用户x和用户y成为了好友 ...
- bzoj4419[SHOI2013]发微博
题意:给你一个初始没有边,点权均为0的无向图,三种操作:加边,删边,选择一个点将当前与之相邻的点(不包括自身)的点权+1,询问最后所有点的点权. 据说正解是set维护每个人的朋友,然后考虑每次加边.删 ...
随机推荐
- python 链表、堆、栈
简介 很多开发在开发中并没有过多的关注数据结构,当然我也是,因此,我写这篇文章就是想要带大家了解一下这些分别是什么东西. 链表 概念:数据随机存储,并且通过指针表示数据之间的逻辑关系的存储结构. 链表 ...
- 如何设计一个分布式 ID 发号器?
大家好,我是树哥. 在复杂的分布式系统中,往往需要对大量的数据和消息进行唯一标识,例如:分库分表的 ID 主键.分布式追踪的请求 ID 等等.于是,设计「分布式 ID 发号器」就成为了一个非常常见的系 ...
- Lua 语言
# Lua是一种轻量.小巧的脚本语言,用标准C语言编写并以源码形式开发.设计的摸底是为了嵌入到其他应用程序中,从而为应用程序提供灵活的拓展和定制功能. # Lua安装 # 官网:https://www ...
- 技术分享 | 浅谈mysql语法解析调试方法
欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 本文向您介绍一种利用mysql解析器和bison的调试选项进行sql语法解析跟踪 ...
- 发布 .NET MAUI / MAUI Blazor 应用 (1) - Windows
发布用于Windows的 .NET MAUI 应用 打开 PowerShell 终端 , 命令行进入工程目录,以我的例子工程为例, cd BlazorMaui BlazorMaui 替换为你自己工程名 ...
- Apache DolphinScheduler 迎来 2 位 PPMC
经过 Apache DolphinScheduler PPMC 们的推荐和投票,我们高兴的宣布:Apache DolphinScheduler 迎来了 2 位 PPMC .他们是(github id) ...
- Luogu3090 [USACO13NOV]空荡荡的摊位Empty Stalls (动态规划)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...
- Docker 01 概述
参考源 https://www.bilibili.com/video/BV1og4y1q7M4?spm_id_from=333.999.0.0 https://www.bilibili.com/vid ...
- 操作系统学习笔记5 | 用户级线程 && 内核级线程
在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...
- 图床搭建|chrome插件|操作简单不要钱
为什么需要一个图床 为了写博客 图床:专门用来存放图片,同时允许你把图片对外连接的网上空间. 用markdown格式写作,插入图片需要图片的url地址,然后博客的背景图也是要用url地址. 有段时间, ...