题解:P14023 [ICPC 2024 Nanjing R] 社交媒体
考虑将每条评论按照 "还需要哪些人成为好友" 来分类。
设初始好友集合为
- 若
a, b \in F ,则这条评论已经可见,计入答案ans 。 - 若恰好一个在
F 中,另一个不在(不妨设a \in F, b \notin F ),则只需选b 为好友即可看到。令c_1[b] 加一,其中c_1[x] 表示只选x 一人就能新增看到的评论数。 - 若
a = b 且a \notin F ,则只需选a 为好友即可看到。令c_1[a] 加一。 - 若
a, b \notin F 且a \neq b ,则必须同时选a 和b 为好友才能看到。令c_2[\{\min(a,b), \max(a,b)\}] 加一,其中c_2[\{x, y\}] 表示必须同时选x 和y 两人才能看到的评论数。
这样,对于任意非好友用户
考虑选择策略。设新选的好友集合为
若
若
不存在一条评论同时属于两类,因为每条评论所需的新好友集合是唯一确定的。
因此,问题转化为求
对于选两人的最大收益,需要分两种情况考虑:
情况一:两人之间有共同评论(
情况二:两人之间没有共同评论(
最后,答案即为
这样分类相当于穷举了所有情况,所以肯定是正确的。
:::success[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
bool vis[N];
int T, n, m, k, c1[N];
map<pair<int, int>, int> c2;
void solve()
{
cin >> n >> m >> k;
c2.clear();
for(int i = 1; i <= k; i++) c1[i] = vis[i] = 0;
for(int i = 1; i <= n; i++)
{
int x;
cin >> x;
vis[x] = 1;
}
int ans = 0;
vector<pair<int,int>> edges;
for(int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
edges.push_back({u, v});
if(vis[u] == 1 && vis[v] == 1) ans++;
else if(vis[u] == 1 && vis[v] == 0) c1[v]++;
else if(vis[u] == 0 && vis[v] == 1) c1[u]++;
else if(u == v) c1[u]++;
}
for(auto &e : edges)
{
int u = e.first, v = e.second;
if(vis[u] == 0 && vis[v] == 0 && u != v)
{
if(u > v) swap(u, v);
c2[{u, v}]++;
}
}
int max1 = 0;
for(int i = 1; i <= k; i++)
{
if(vis[i] == 0) max1 = max(max1, c1[i]);
}
int max2 = 0;
for(auto &it : c2)
{
int u = it.first.first, v = it.first.second;
max2 = max(max2, c1[u] + c1[v] + it.second);
}
int mx1 = 0, mx2 = 0;
for(int i = 1; i <= k; i++)
{
if(vis[i] == 0)
{
if(c1[i] > mx1) mx2 = mx1, mx1 = c1[i];
else if(c1[i] > mx2) mx2 = c1[i];
}
}
max2 = max(max2, mx1 + mx2);
cout << ans + max({0, max1, max2}) << "\n";
return;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
cin >> T;
while(T--) solve();
return 0;
}
:::