Codeforces Round #267 Div.2 D Fedor and Essay -- 强连通 DFS

题意:给一篇文章,再给一些单词替换关系a b,表示单词a可被b替换,可多次替换,问最后把这篇文章替换后(或不替换)能达到的最小的‘r‘的个数是多少,如果‘r‘的个数相等,那么尽量是文章最短。

解法:易知单词间有二元关系,我们将每个二元关系建有向边,然后得出一张图,图中可能有强连通分量(环等),所以找出所有的强连通分量缩点,那个点的minR,Len赋为强连通分量中最小的minR,Len,然后重新建图,跑一个dfs即可得出每个强连通分量的minR,Len,最后O(n)扫一遍替换单词,统计即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
#include <map>
#define INF 0x3f3f3f3f
#define lll __int64
using namespace std;
#define N 100007

struct Edge
{
    int v,next;
}G[4*N],G2[4*N];
string ss[N];
map<string,int> mp;
int minR[4*N],Len[4*N];
int nR[4*N],nLen[4*N];
int head[4*N],tot,cnt,vis[4*N];
int last[4*N],tot2;
stack<int> stk;
int instk[4*N],now,Time;
int low[4*N],dfn[4*N],bel[4*N];
lll sumR,sumLen;

void addedge(int u,int v)
{
    G[tot].v = v;
    G[tot].next = head[u];
    head[u] = tot++;
}

void addedge2(int u,int v)  //建新图
{
    G2[tot2].v = v;
    G2[tot2].next = last[u];
    last[u] = tot2++;
}

void tarjan(int u)
{
    low[u] = dfn[u] = ++Time;
    stk.push(u);
    instk[u] = 1;
    for(int i=head[u];i!=-1;i=G[i].next)
    {
        int v = G[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(instk[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        cnt++;
        int v;
        do{
            v = stk.top();
            stk.pop();
            instk[v] = 0;
            bel[v] = cnt;
            if(minR[v] < nR[cnt] || (minR[v] == nR[cnt] && Len[v] < nLen[cnt]))
                nR[cnt] = minR[v],nLen[cnt] = Len[v];
        }while(u != v);
    }
}

void Tarjan()
{
    memset(bel,0,sizeof(bel));
    memset(instk,0,sizeof(instk));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    Time = 0,cnt = 0;
    while(!stk.empty()) stk.pop();
    int i;
    for(i=1;i<=now;i++)
        if(!dfn[i])
            tarjan(i);
}

void Build()
{
    int i,j;
    memset(last,-1,sizeof(last));
    tot2 = 0;
    for(i=1;i<=now;i++)
    {
        for(j=head[i];j!=-1;j=G[j].next)
        {
            int v = G[j].v;
            if(bel[i] != bel[v])
                addedge2(bel[i],bel[v]);
        }
    }
}

void dfs(int u)
{
    if(vis[u]) return;
    vis[u] = 1;
    for(int i=last[u];i!=-1;i=G2[i].next)
    {
        int v = G2[i].v;
        dfs(v);
        if((nR[v] < nR[u]) || (nR[v] == nR[u] && nLen[v] < nLen[u]))
            nR[u] = nR[v],nLen[u] = nLen[v];
    }
}

int main()
{
    int n,m,i,j,len;
    while(scanf("%d",&n)!=EOF)
    {
        now = 0;
        mp.clear();
        tot = 0;
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        for(i=1;i<=n;i++)
        {
            cin>>ss[i];
            len = ss[i].length();
            int cntR = 0;
            for(j=0;j<len;j++)
            {
                if(ss[i][j] >= ‘A‘ && ss[i][j] <= ‘Z‘)
                    ss[i][j] = ss[i][j]-‘A‘+‘a‘;
                if(ss[i][j] == ‘r‘)
                    cntR++;
            }
            if(!mp[ss[i]])
                mp[ss[i]] = ++now;
            Len[mp[ss[i]]] = len;
            minR[mp[ss[i]]] = cntR;
        }
        scanf("%d",&m);
        string sa,sb;
        for(i=1;i<=m;i++)
        {
            sa = "", sb = "";
            cin>>sa>>sb;
            len = sa.length();
            int cntR = 0;
            for(j=0;j<len;j++)
            {
                if(sa[j] >= ‘A‘ && sa[j] <= ‘Z‘)
                    sa[j] = sa[j]-‘A‘+‘a‘;
                if(sa[j] == ‘r‘)
                    cntR++;
            }
            if(!mp[sa])
                mp[sa] = ++now;
            int a = mp[sa];
            Len[a] = len;
            minR[a] = cntR;

            len = sb.length();
            cntR = 0;
            for(j=0;j<len;j++)
            {
                if(sb[j] >= ‘A‘ && sb[j] <= ‘Z‘)
                    sb[j] = sb[j]-‘A‘+‘a‘;
                if(sb[j] == ‘r‘)
                    cntR++;
            }
            if(!mp[sb])
                mp[sb] = ++now;
            int b = mp[sb];
            Len[b] = len;
            minR[b] = cntR;
            addedge(a,b);
        }
        memset(nR,INF,sizeof(nR));     //新图的点的minR,Len
        memset(nLen,INF,sizeof(nLen));
        Tarjan();
        Build();
        for(i=1;i<=now;i++)
            if(!vis[i])
                dfs(i);
        sumR = 0,sumLen = 0;
        for(i=1;i<=n;i++)
        {
            int u = bel[mp[ss[i]]];
            sumR += nR[u];
            sumLen += nLen[u];
        }
        printf("%I64d %I64d\n",sumR,sumLen);
    }
    return 0;
}

还有一种做法就是反向建图,然后sort一下,优先从最优的开始dfs,最后就能得出结果,但是我不知道这样为什么一定正确,如果你知道,那么请评论告诉我吧:)

时间: 2024-11-04 13:12:59

Codeforces Round #267 Div.2 D Fedor and Essay -- 强连通 DFS的相关文章

Codeforces Round #267 (Div. 2) B. Fedor and New Game

After you had helped George and Alex to move in the dorm, they went to help their friend Fedor play a new computer game ?Call of Soldiers 3?. The game has (m?+?1) players and n types of soldiers in total. Players ?Call of Soldiers 3? are numbered for

Codeforces Round #267 (Div. 2) C. George and Job(DP)补题

Codeforces Round #267 (Div. 2) C. George and Job题目链接请点击~ The new ITone 6 has been released recently and George got really keen to buy it. Unfortunately, he didn't have enough money, so George was going to work as a programmer. Now he faced the follow

01背包 Codeforces Round #267 (Div. 2) C. George and Job

题目传送门 1 /* 2 题意:选择k个m长的区间,使得总和最大 3 01背包:dp[i][j] 表示在i的位置选或不选[i-m+1, i]这个区间,当它是第j个区间. 4 01背包思想,状态转移方程:dp[i][j] = max (dp[i-1][j], dp[i-m][j-1] + sum[i] - sum[i-m]); 5 在两个for循环,每一次dp[i][j]的值都要更新 6 */ 7 #include <cstdio> 8 #include <cstring> 9 #i

Codeforces Round #267 (Div. 2)

A.George and Accommodation 题意:给定数组a,b,问b-a>=2有多少个 思路:直接模拟.. B.Fedor and New Game 题意:给定m+1个n位以内的二进制数,求前m个有多少个跟第m+1的二进制下不同位数不超过k的个数 思路:直接模拟 C.George and Job 题意:给定n个数,求k个的段,每段有m个连续得数,使得和最大 思路:直接dp,注意long long不会超内存 1 /* 2 * Author: Yzcstc 3 * Created Tim

Codeforces Round #267 (Div. 2) solution

A.George and Accommodation 题目大意:给你n个宿舍的可以容纳的人数和现在已住的人数,问有几个宿舍可以安排两个人进去住. 解法:模拟,无trick. 代码: 1 #include <cstdio> 2 3 int main() { 4 int n; 5 while(scanf("%d", &n) != EOF){ 6 int a, b, ans = 0; 7 for(int i = 0; i < n; i++) { 8 scanf(&q

Codeforces Round #267 (Div. 2) B

题目: B. Fedor and New Game time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output After you had helped George and Alex to move in the dorm, they went to help their friend Fedor play a new computer

Codeforces Round #267 (Div. 2) C. George and Job

The new ITone 6 has been released recently and George got really keen to buy it. Unfortunately, he didn't have enough money, so George was going to work as a programmer. Now he faced the following problem at the work. Given a sequence of n integers p

Codeforces Round #267 (Div. 2) 水了一发 真.记录

闲着没事就水了一发DIV2,本来想新注册一个小小号来着.结果验证码一直显示不出来.于是就用小号做. 结果rank44,但是没有rating. 以下均不解释题意了. A:O(n)脑残模拟. Code: #include <cstdio> int main() { int n, a, b; scanf("%d", &n); int res = 0; while(n--) { scanf("%d%d", &a, &b); if (a +

Codeforces Round #267 (Div. 2) A. George and Accommodation

George has recently entered the BSUCP (Berland State University for Cool Programmers). George has a friend Alex who has also entered the university. Now they are moving into a dormitory. George and Alex want to live in the same room. The dormitory ha