CF467D 题解
思路
这道题很水,常见的套图题。
首先,我们把所有字符串都变成点,可以采用映射或者字典树,后者可以做到时间复杂度和输入规模一样。
接着,我们采用强连通缩点,并设 $f_u$ 为 $u$ 点可以变成的字符串的最小 R 数量,而 $g_u$ 则表示 $u$ 点在 $f_u$ 最小时最小的单词长度。的由于同一个强连通分量的点之间两两可达,所以一个强连通分量变成点后的最小 R 数量就是此强连通分量中每个点 R 数量的最小值。
最后,我们建反图跑有向无环图上动态规划即可,中间转移取他自己的 R 数量和所有能到达他的点中的动态规划数组最小值。
代码
#include <bits/stdc++.h>
#define ll long long
#define L(i, a, b) for(int i = a; i <= b; i++)
#define R(i, a, b) for(int i = a; i >= b; i--)
using namespace std;
const int N = 3e5 + 10; int m, n, totp, tot, top, col, a[N]; ll ans1, ans2;
int stk[N], in[N], vl[N], cl[N], dfn[N], low[N], vis[N], f[N], g[N], ln[N];
queue<int> q; vector<int> eg[N], e[N]; unordered_map<string, int> mp;
void Upd(int x, int fy, int gy){
if(fy < f[x]) f[x] = fy, g[x] = gy;
else if(f[x] == fy) g[x] = min(g[x], gy);
}
int get(string s){
L(i, 0, s.size() - 1) s[i] += (s[i] <= 'Z'? 32 : 0);
if(mp[s]) return mp[s]; mp[s] = ++totp;
L(i, 0, s.size() - 1) vl[totp] += (s[i] == 'r');
ln[totp] = s.size(); return totp;
}
void Tarjan(int u){
dfn[u] = low[u] = ++tot, vis[stk[++top] = u] = 1;
for(int v: eg[u]){
if(!dfn[v])
Tarjan(v), low[u] = min(low[u], low[v]);
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]){
col++;
while(stk[top] != u)
vis[stk[top]] = 0, cl[stk[top--]] = col;
vis[u] = 0, cl[stk[top--]] = col;
}
}
int main(){
string s, c; scanf("%d", &m);
L(i, 1, m) cin >> s, a[i] = get(s);
scanf("%d", &n);
L(i, 1, n) cin >> s >> c, eg[get(s)].push_back(get(c));
L(i, 1, totp) if(!dfn[i]) Tarjan(i); L(i, 1, tot) f[i] = g[i] = 1e9;
L(u, 1, totp){
Upd(cl[u], vl[u], ln[u]);
for(int v: eg[u]) if(cl[u] ^ cl[v])
e[cl[v]].push_back(cl[u]), in[cl[u]]++;
}
L(i, 1, tot) if(!in[i]) q.push(i);
while(!q.empty()){
int u = q.front(); q.pop();
for(int v: e[u]){
in[v]--, Upd(v, f[u], g[u]);
if(!in[v]) q.push(v);
}
}
L(i, 1, m) ans1 += f[cl[a[i]]], ans2 += g[cl[a[i]]];
printf("%lld %lld\n", ans1, ans2);
return 0;
}