Codeforces_467D
题意
给你一个字符串,里面有 R 和 r 在单词中的出现数量尽可能少,在 R 和 r 尽可能少的情况下使得最后所有单词长度之和最小。
思路
一开始将大写全部改成小写,利用 map 记录每个单词的编号,再 tarjan 把单词缩一下,因为如果单词太多,会不太好做,缩成 Dag 后会简便很多,缩的时候要记得对于每个连通分量记录 r 的最少个数以及单词的最短的长度,最后进行一次拓扑排序求出答案,如果不会 tarjan,请前往 P3387 学习,最后输出即可。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500010;
//答案
int res_l[N];
int res_r[N];
int idx;
int hr[N];
string a[N];
vector<int> P[N];
map<string, int> id;
vector<int> graph[N];
map<int, string> idxt;
//tarjan
bool in_stk[N];
int scc_cnt, top, timestamp;
int dfn[N], low[N], stk[N], idt[N], in[N];
//将大写改成小写
void do_(string& s)
{
for (int i = 0; i < s.size(); i ++ )
if ('A' <= s[i] && s[i] <= 'Z') s[i] = (char)(s[i] - 'A' + 'a');
}
//统计r的个数
void do__(string s)
{
for (int i = 0; i < s.size(); i ++ )
if (s[i] == 'r') hr[id[s]] ++ ;
}
//tarjan
void tarjan(int u)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u, in_stk[u] = true;
for (int j : graph[u])
{
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if (in_stk[j])
low[u] = min(low[u], dfn[j]);
}
if (dfn[u] == low[u])
{
++ scc_cnt;
int y;
do {
y = stk[top -- ];
in_stk[y] = false;
idt[y] = scc_cnt;
if (res_r[scc_cnt] == hr[y])
res_l[scc_cnt] = min(res_l[scc_cnt], (int)(idxt[y].size()));
if (res_r[scc_cnt] > hr[y])
{
res_r[scc_cnt] = hr[y];
res_l[scc_cnt] = idxt[y].size();
}
} while (y != u);
}
}
signed main()
{
memset(res_l, 0x3f, sizeof res_l);
memset(res_r, 0x3f, sizeof res_r);
int n;
cin >> n;
//输入处理
for (int i = 1; i <= n; i ++ )
{
cin >> a[i]; do_(a[i]);
if (id[a[i]] == 0) id[a[i]] = ++ idx, do__(a[i]), idxt[idx] = a[i];
}
int T;
cin >> T;
while (T -- )
{
string s, t;
cin >> s >> t;
do_(s), do_(t);
if (id[s] == 0) id[s] = ++ idx, do__(s), idxt[idx] = s;
if (id[t] == 0) id[t] = ++ idx, do__(t), idxt[idx] = t;
graph[id[s]].push_back(id[t]);
}
for (int i = 1; i <= idx; i ++ )
if (!dfn[i]) tarjan(i);
//缩点
for (int i = 1; i <= idx; i ++ )
for (int j = 0; j < graph[i].size(); j ++ )
if (idt[graph[i][j]] != idt[i])
P[idt[graph[i][j]]].push_back(idt[i]), in[idt[i]] ++ ;
queue<int> q;
for (int i = 1; i <= scc_cnt; i ++ )
if (!in[i]) q.push(i);
//拓扑序
while (!q.empty())
{
int u = q.front();
q.pop();
for (int v : P[u])
{
in[v] -- ;
if (res_r[v] == res_r[u]) res_l[v] = min(res_l[v], res_l[u]);
if (res_r[v] > res_r[u]) res_r[v] = res_r[u], res_l[v] = res_l[u];
if (!in[v]) q.push(v);
}
}
int res_len = 0, res_rs = 0;
for (int i = 1; i <= n; i ++ )
res_len += res_l[idt[id[a[i]]]], res_rs += res_r[idt[id[a[i]]]];
cout << res_rs << " " << res_len;
return 0;
}