模拟+数据结构:上海省选2013 P3998题解
P3998 [SHOI2013] 发微博
01.题意理解
进行$m$次操作,操作分为$3$种:
\(01.发布一条说说\)
\(02.和另一个用户建立朋友关系\)
\(03.和另一个用户解除朋友关系\)
求$n$个人最后各自可以看见多少条说说
02.思路分析
思路1$(10pts)$:
对于$n≤1000且m≤1000$ 思路显然:
输入$m$次操作
01.操作为$2$时:每次输入$a,b$并建边$[a,b]$
02.操作为$3$时:删边$[a,b]$
03.操作为$1$时:输入$a$,将所有与$a$相连的点看见的说说数量$ans[i]$加1
最后输出$ans[1$~$n]$即可,复杂度$O(mn)$
思路2:\((100pts)\)
对于$n≤30000且m≤30000$ 考虑降低时间复杂度
$O(m)$是难以被降低的,故考虑将$O(mn)$降为$O(m)$
那么就可以形成新的思路:
前置:
建立一个二维$vector$数组$edge$,定义$edge[Node1][i]$表示$Node1$结点所连接的一个节点
那么我们可以用一些常用的数组操作来记录每个节点在$edge[x]$中的位置$id$
同时可以建立$d$数组来存储每个点已经发送的信息数量
显然我们可以$O(1)$时间完成对任意边的查询
则有:
01.操作$2$时:建边$[a,b]$,在$edge[a]$中push_back此时的$d[a]$,存储$id[a][b]$
注意:$vector$内存储的时$d[a]$而非$b$
02.操作$3$时:删边$[a,b]$,将$ans[b]$存储为$d[a]-edge[a]$
03.操作$1$时:将$d[a]++$
注意: 操作完成后可能仍有边未删除,此时需要额外进行删边操作
#include<bits/stdc++.h>
#define int long long
const int MAXN=2e5+10;
using namespace std;
struct E
{
int u,v;
};
E arr[MAXN];
vector<int> edge[MAXN];
unordered_map<int,unordered_map<int,bool> > vis;
unordered_map<int,unordered_map<int,int> > id,id2;
int d[MAXN],ans[MAXN];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n,m,cnt=0;
cin>>n>>m;
while(m--)
{
// cout<<"TTT"<<"\n";
char opt;
cin>>opt;
if(opt=='!')
{
int u;
cin>>u;
++d[u];
}
else if(opt=='+')//建边
{
int u,v;
cin>>u>>v;
// cout<<"2\n";
if(vis[u][v]==0)
{
//记录v在u中的位置
id[u][v]=edge[u].size();
id[v][u]=edge[v].size();
//建图
edge[u].push_back(d[u]);
edge[v].push_back(d[v]);
vis[u][v]=vis[v][u]=1;
int u2=min(u,v);
int v2=max(u,v);
// id2[u2][v2]=arr.size();
arr[++cnt]={u2,v2};
}
}
else//删边
{
int u,v;
cin>>u>>v;
if(vis[u][v]==1)
{
int sd=edge[u][id[u][v]];
ans[v]+=d[u]-sd;
sd=edge[v][id[v][u]];
ans[u]+=d[v]-sd;
vis[u][v]=vis[v][u]=0;
int u2=min(u,v);
int v2=max(u,v);
}
}
}
// cout<<"1\n";
for(int i=1;i<=cnt;++i)
{
int u=arr[i].u,v=arr[i].v;
if(vis[u][v])
{
int sd=edge[u][id[u][v]];
ans[v]+=d[u]-sd;
sd=edge[v][id[v][u]];
ans[u]+=d[v]-sd;
vis[u][v]=vis[v][u]=0;
}
}
for(int i=1;i<=n;++i)
cout<<ans[i]<<" ";
}

浙公网安备 33010602011771号