CF467D Fedor and Essay 有向图强连通分量+缩点

一.题目

传送门
翻译:
在你帮助Fedor在«Call of Soldiers 3»这款游戏中找到朋友之后,他完全停止了学习。今天,英语老师让他准备一篇文章。 Fedor并不想准备这篇文章,所以他向Alex寻求帮助。Alex来帮忙并为Fedor写了一篇文章。但Fedor根本不喜欢这篇文章。现在Fedor将使用英语同义词词典来重写文章。

Fedor不想改变文章的含义。因此,他将做的唯一改变是:根据字典中的替换规则,将一个论文中的单词改为它的一个同义词。 Fedor可以多次执行此操作。

因此,Fedor希望得到一篇文章,其中包含尽可能少的字母“R”(样例无关)。如果有多篇论文都有最小数量的“R”,那么他希望得到一个最小长度的论文(论文的长度是其中所有单词长度的总和)。帮助Fedor获得所需的论文。

请注意,在这个问题中,字母的大小写情况无关紧要。例如,如果同义词词典说单词cat可以用单词DOG替换,则允许用单词doG替换单词Cat。

二.题解

乍一看一个字符串的题跟强连通分量有什么关系?QWQ
其实关系大。对于每一个单词向可以替换它的单词连一条有向边,每个单词内r的数量即点权,然后我们用minn1[i]minn1[i]存每个连通块内的单词中字母r的最少个数,用minn2[i]minn2[i]存每个连通块内的单词的最短长度。
先做一遍Tarjan求有向图强连通分量,然后再缩点做一个树形DP,用来更新minn1[i],minn2[i]minn1[i],minn2[i]。这道题就愉快地解决了。
至于存单词,用map就行了。

三.Code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <map>
using namespace std;

#define M 500005
#define LL long long

int n, m, dfn[M], low[M], cnt, num, val[M], len[M], sz, minn1[M], minn2[M], belong[M], id[M];
bool vis[M], instack[M];
LL ans1, ans2;
vector <int> G[M];
vector <int> G1[M];
stack <int> s;
map <string, int> mp;

void Tarjan (int x){
    dfn[x] = low[x] = ++ cnt;
    s.push(x);
    instack[x] = 1;
    for (int i = 0; i < G[x].size(); i ++){
        int tmp = G[x][i];
        if (! dfn[tmp]){
            Tarjan (tmp);
            low[x] = min (low[x], low[tmp]);
        }
        else if (instack[tmp]){
            low[x] = min (low[x], dfn[tmp]);
        }
    }
    if (low[x] == dfn[x]){
        num ++;
        int v;
        do{
            v = s.top();
            s.pop();
            instack[v] = 0;
            belong[v] = num;
            if (val[v] < minn1[num] || (val[v] == minn1[num] && len[v] < minn2[num]))
                minn1[num] = val[v], minn2[num] = len[v];
        }while (v != x);
    }
}
void dp (int x){
    if (vis[x])
        return ;
    vis[x] = 1;
    for (int i = 0; i < G1[x].size(); i ++){
        int tmp = G1[x][i];
        dp (tmp);
        if (minn1[tmp] < minn1[x])
            minn1[x] = minn1[tmp], minn2[x] = minn2[tmp];
        else if (minn1[tmp] == minn1[x])
                minn2[x] = min (minn2[x], minn2[tmp]);
    }
}
int main (){
    memset (minn1, 0x3f, sizeof minn1);
    memset (minn2, 0x3f, sizeof minn2);
    scanf ("%d", &m);
    for (int i = 1; i <= m; i ++){
        char ch[M];
        scanf ("%s", ch);
        int l = strlen (ch), ns = 0;
        for (int j = 0; j < l; j ++){
            if (ch[j] >= 'A' && ch[j] <= 'Z')   ch[j] = ch[j] - 'A' + 'a';
            if (ch[j] == 'r')   ns ++;
        }
        int t = mp[ch];
        if (! t)
            mp[ch] = t = ++ sz;
        val[t] = ns, len[t] = l;
        id[i] = t;
    }
    scanf ("%d", &n);
    for (int i = 1; i <= n; i ++){
        char ch[M];
        scanf ("%s", ch);
        int x = strlen (ch), s = 0;
        for (int j = 0; j < x; j ++){
            if (ch[j] >= 'A' && ch[j] <= 'Z') ch[j] = ch[j] - 'A' + 'a';
            if (ch[j] == 'r')  s ++;
        }
        int t1 = mp[ch];
        if (! t1)
            mp[ch] = t1 = ++ sz;
        val[t1] = s, len[t1] = x;
        scanf ("%s", ch);
        x = strlen (ch), s = 0;
        for (int j = 0; j < x; j ++){
            if (ch[j] >= 'A' && ch[j] <= 'Z') ch[j] = ch[j] - 'A' + 'a';
            if (ch[j] == 'r')  s ++;
        }
        int t2 = mp[ch];
        if (! t2)
            mp[ch] = t2 = ++ sz;
        val[t2] = s, len[t2] = x;
        G[t1].push_back (t2);
    }
    for (int i = 1; i <= sz; i ++)
        if (! dfn[i])
            Tarjan (i);
    for (int i = 1; i <= sz; i ++){
        for (int j = 0; j < G[i].size (); j ++){
            int tmp = G[i][j];
            if (belong[i] != belong[tmp])
                G1[belong[i]].push_back (belong[tmp]);
        }
    }
    for (int i = 1; i <= num; i ++)
        dp (i);
    for (int i = 1; i <= m; i ++)
        ans1 = ans1 + 1ll * minn1[belong[id[i]]], ans2 = ans2 + 1ll * minn2[belong[id[i]]];
    printf ("%lld %lld\n", ans1, ans2);
    return 0;
}

Thanks!

posted on 2020-08-21 08:34  PI_PJW  阅读(41)  评论(0)    收藏  举报

导航