[ABC157D] Friend Suggestions 题解

题目大意

共有 n n n 个用户,给出 m m m 组互关和 k k k 组互黑关系,可化为两个图。对于每一个互关关系连通块,原来没有互关的人可以互关,除了两人已经互黑。这样的人成为推荐用户。

求每个人有几个推荐用户。

方法

考虑深度优先搜索加上容斥原理的方法。

深度优先搜索求出每个连通块的用户数量 n u m i num_i numi,即为用户 i i i,与用户 i i i 互关的用户,与用户 i i i 互黑的用户和推荐用户。

可列得式子:
推荐用户 i = n u m i − 与用户 i 互关的人数 − 与用户 i 互黑的人数 − 1 \boxed{\text{推荐用户}_i = num_i - \text{与用户}_i \text{互关的人数} - \text{与用户}_i \text{互黑的人数} - 1} 推荐用户i=numi与用户i互关的人数与用户i互黑的人数1

现在只要求在一个连通块中与用户 i i i 互黑的人数即可。

考虑类似并查集做法,记好每个用户属于哪个连通块,然后枚举与用户 i i i 互黑的用户,如果两个用户在一个连通块里,即可减掉这个假的推荐用户。

代码

#include<bits/stdc++.h>//dfs+容斥原理做法 
using namespace std;
inline void read(int &x)
{
	int f=1;
	char ch=getchar();
	x=0;
	for(;ch>57 || ch<48;ch=getchar()) if(ch==45) f=-1;
	for(;ch<=57 && ch>=48;ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
	x=x*f;
}
int n,m,k,dis[100010],f[100010],fa[100010],cnt,num[100010];
vector<int> at[100010],bl[100010];
inline void search(int pos)
{
	f[pos]=1,fa[pos]=cnt,num[cnt]++;
	bool flag=false;
	for(register int i=0;i<at[pos].size();i++)
		if(!f[at[pos][i]]) search(at[pos][i]),flag=true;
}
int main()
{
	ios::sync_with_stdio(false);
	read(n),read(m),read(k);
	for(register int i=1,x,y;i<=m;i++)read(x),read(y),at[x].push_back(y),at[y].push_back(x);
	for(register int i=1,x,y;i<=k;i++)read(x),read(y),bl[x].push_back(y),bl[y].push_back(x);
	for(register int i=1;i<=n;i++) if(!f[i]) cnt++,search(i);
	for(register int i=1;i<=n;i++) dis[i]=num[fa[i]];
	for(register int i=1;i<=n;i++)
		for(register int j=0;j<bl[i].size();j++) if(fa[i]==fa[bl[i][j]]) dis[i]--;
	for(register int i=1;i<=n;i++) cout<<dis[i]-at[i].size()-1<<' ';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值