Codeforces 467D. Fedor and Essay (Graphs,dfs,dp,hashing,strings,图论综合型好题)

来源:互联网 发布:程序员思维笑话 编辑:程序博客网 时间:2024/05/16 08:02

D. Fedor and Essay
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

After you had helped Fedor to find friends in the «Call of Soldiers 3» game, he stopped studying completely. Today, the English teacher told him to prepare an essay. Fedor didn't want to prepare the essay, so he asked Alex for help. Alex came to help and wrote the essay for Fedor. But Fedor didn't like the essay at all. Now Fedor is going to change the essay using the synonym dictionary of the English language.

Fedor does not want to change the meaning of the essay. So the only change he would do: change a word from essay to one of its synonyms, basing on a replacement rule from the dictionary. Fedor may perform this operation any number of times.

As a result, Fedor wants to get an essay which contains as little letters «R» (the case doesn't matter) as possible. If there are multiple essays with minimum number of «R»s he wants to get the one with minimum length (length of essay is the sum of the lengths of all the words in it). Help Fedor get the required essay.

Please note that in this problem the case of letters doesn't matter. For example, if the synonym dictionary says that word cat can be replaced with word DOG, then it is allowed to replace the word Cat with the word doG.

Input

The first line contains a single integer m (1 ≤ m ≤ 105) — the number of words in the initial essay. The second line contains words of the essay. The words are separated by a single space. It is guaranteed that the total length of the words won't exceed 105 characters.

The next line contains a single integer n (0 ≤ n ≤ 105) — the number of pairs of words in synonym dictionary. The i-th of the next n lines contains two space-separated non-empty words xi and yi. They mean that word xi can be replaced with word yi (but not vise versa). It is guaranteed that the total length of all pairs of synonyms doesn't exceed 5·105 characters.

All the words at input can only consist of uppercase and lowercase letters of the English alphabet.

Output

Print two integers — the minimum number of letters «R» in an optimal essay and the minimum length of an optimal essay.

Sample test(s)
input
3AbRb r Zz4xR abRbaA xrzz Zxr y
output
2 6
input
2RuruRu fedya1ruruRU fedor
output
1 10

题目地址:http://codeforces.com/contest/467/problem/D

别怪我=。=

我本来是写好了题解的,结果按错一个键,题解没有保存下来。

懒,不写了。总的来说方法就是标题所列,那也是codeforces上tag所给

方法如下:

hashing + strings: 有效hash处理字符串,将字符串转换为点,方便图形处理(要以字符串种类为基建图,否则边会太多,数组存不下

graphs:  图论,用到建图等图论知识

SCC: 强联通分量,将图转换为有向无环图(直接处理图会出错,必须转换为有向无环图

dfs+dp: 在有向无环图上进行dfs,在dfs基础上dp,进行搜索

最后统计答案即可

看不懂就抱歉了,不小心没保存题解

据说这题有更好的方法?我要去学学,哪天心血来潮再把题解补上吧=。=!

//Hello. I'm Peter.#include<cstdio>#include<iostream>#include<sstream>#include<cstring>#include<string>#include<cmath>#include<cstdlib>#include<algorithm>#include<functional>#include<cctype>#include<ctime>#include<stack>#include<queue>#include<vector>#include<set>#include<map>using namespace std;typedef long long ll;typedef long double ld;#define peter cout<<"i am peter"<<endl#define input freopen("data.txt","r",stdin)#define randin srand((unsigned int)time(NULL))#define INT (0x3f3f3f3f)*2#define LL (0x3f3f3f3f3f3f3f3f)*2#define gsize(a) (int)a.size()#define len(a) (int)strlen(a)#define slen(s) (int)s.length()#define pb(a) push_back(a)#define clr(a) memset(a,0,sizeof(a))#define clr_minus1(a) memset(a,-1,sizeof(a))#define clr_INT(a) memset(a,INT,sizeof(a))#define clr_true(a) memset(a,true,sizeof(a))#define clr_false(a) memset(a,false,sizeof(a))#define clr_queue(q) while(!q.empty()) q.pop()#define clr_stack(s) while(!s.empty()) s.pop()#define rep(i, a, b) for (int i = a; i < b; i++)#define dep(i, a, b) for (int i = a; i > b; i--)#define repin(i, a, b) for (int i = a; i <= b; i++)#define depin(i, a, b) for (int i = a; i >= b; i--)#define pi 3.1415926535898#define eps 1e-6#define MOD 1000000007#define MAXN 100100#define N#define Mint m,n;//下面的是用于原图的,即没有SCC之前的图struct Vertex{    ll len,numr;}ver[3*MAXN];int num_ver;struct Edge{    int to,next,from;}edge[10*MAXN];int w,head[3*MAXN];void build_edge(int from,int to){    w++;    edge[w].from=from;    edge[w].to=to;    edge[w].next=head[from];    head[from]=w;}map<string,int>mymap;map<string,int>::iterator it,it1,it2;ll numr;string makelowercasestring(string s){//将string全部转为小写,顺便统计 r 的个数    int len=slen(s);    string res;    res.clear();    numr=0;    rep(i,0,len)    {        if(isupper(s[i])) s[i]=s[i]-'A'+'a';        res+=s[i];        if(s[i]=='r') numr++;    }    return res;}void makemap(string s,int t){    mymap.insert(pair<string,int>(s,t));}int pre[3*MAXN],lowlink[3*MAXN],sccbelong[3*MAXN];ll sccnum[3*MAXN];int dfs_clock,scc_cnt;stack<int>st;void tarjan(int now){//缩点,将图变成有向无环图    pre[now]=lowlink[now]=++dfs_clock;    st.push(now);    int i,to;    for(i=head[now];i!=-1;i=edge[i].next)    {        to=edge[i].to;        if(!pre[to])        {            tarjan(to);            lowlink[now]=min(lowlink[now],lowlink[to]);        }        else if(!sccbelong[to])        {            lowlink[now]=min(lowlink[now],pre[to]);        }    }    if(lowlink[now]==pre[now])    {        scc_cnt++;        int x;        do        {            x=st.top();            st.pop();            sccbelong[x]=scc_cnt;            sccnum[scc_cnt]++;        }while(now!=x);    }}//下面是用于强联通分量的图,用于新图的struct Vertextarjan{    ll len,numr;}vertarjan[3*MAXN];int num_verr;struct Edgetarjan{    int to,next,from;}edgetarjan[10*MAXN];int headtarjan[3*MAXN],ww;void build_edgetarjan(int from,int to){    ww++;    edgetarjan[ww].from=from;    edgetarjan[ww].to=to;    edgetarjan[ww].next=headtarjan[from];    headtarjan[from]=ww;}bool vis[3*MAXN];void dfs(int now){//dfs搜答案,动态规划处理    vis[now]=true;    int i,to;    for(i=headtarjan[now];i!=-1;i=edgetarjan[i].next)    {        to=edgetarjan[i].to;        if(!vis[to]) dfs(to);        if(vertarjan[now].numr>vertarjan[to].numr)        {            vertarjan[now].numr=vertarjan[to].numr;            vertarjan[now].len=vertarjan[to].len;        }        else if(vertarjan[now].numr==vertarjan[to].numr && vertarjan[now].len>vertarjan[to].len)        {            vertarjan[now].len=vertarjan[to].len;        }    }}string forinput[3*MAXN];//记录输入,统计答案用int main(){    cin>>m;    string s;    mymap.clear();    num_ver=0;    repin(i,1,m)    {        cin>>s;        s=makelowercasestring(s);        forinput[i]=s;        it=mymap.find(s);        if(it!=mymap.end()) continue;//由于以种类为基建图,所以同种类型只让其出现一次        makemap(s,++num_ver);        int t=num_ver;        ver[t].len=slen(s);        ver[t].numr=numr;    }    cin>>n;    string sx,sy;    int x,y;    w=0;    clr_minus1(head);    //下面,字符串hash后,以字符串种类为单位建图    repin(ii,1,n)    {        cin>>sx>>sy;        sx=makelowercasestring(sx);        it=mymap.find(sx);        if(it==mymap.end())        {            makemap(sx,++num_ver);            int t=num_ver;            ver[t].len=slen(sx);            ver[t].numr=numr;            it=mymap.find(sx);        }        x=it->second;        sy=makelowercasestring(sy);        it=mymap.find(sy);        if(it==mymap.end())        {            makemap(sy,++num_ver);            int t=num_ver;            ver[t].len=slen(sy);            ver[t].numr=numr;            it=mymap.find(sy);        }        y=it->second;        build_edge(x,y);    }    //此时,所有的点和边都适当地建立好了    //下面就开始SCC,将图变成有向无环图    dfs_clock=scc_cnt=0;    repin(i,1,num_ver)    {        if(!pre[i]) tarjan(i);    }    //下面将tarjan出来的有向无环图建立起来    vector<int>belong[3*MAXN];    //记录每个点属于哪一个强联通分量    repin(i,1,num_ver)    {        int be=sccbelong[i];        belong[be].pb(i);    }    ll len=1;    num_verr=0;    //下面这一步很重要    //将每个强联通分量构建出来,其numr和len值来源于其中的点    //找到其中点numr最小的那个,numr一样找len最小那个,那个点就代表着这个强联通分量,相当于其他点都视为那个最优点    repin(ii,1,scc_cnt)    {        int len1=gsize(belong[ii]);        bool first=true;        rep(j,0,len1)        {            int i=belong[ii][j];            if(first)            {                first=false;                len=ver[i].len;                numr=ver[i].numr;                continue;            }            if(ver[i].numr<numr)            {                numr=ver[i].numr;                len=ver[i].len;            }            else if(ver[i].numr==numr && ver[i].len<len)            {                len=ver[i].len;            }        }        num_verr++;//ii==num_verr        vertarjan[ii].len=len;        vertarjan[ii].numr=numr;    }    //点建好了,根据之前的边的关系找桥,实际上就是找割点,通过桥构建新图的边    //这是将图转换为有向无环图的重要有效方法    clr_minus1(headtarjan);    ww=0;    int from,to;    repin(i,1,w)    {        from=edge[i].from;        to=edge[i].to;        if(sccbelong[from]!=sccbelong[to])        {            int x1=sccbelong[from],x2=sccbelong[to];            build_edgetarjan(x1,x2);        }    }    //dfs+dp搜    repin(i,1,num_verr)    {        if(!vis[i]) dfs(i);    }    //统计答案    ll ans1=0,ans2=0;    int be,wh;    repin(i,1,m)    {//对于输入的这个字符串来说,找到它属于的强联通分量,SCC的答案就是它的答案     //注意建图时候是以种类为基来建图的,但一个种类的字符串可能代表很多个字符串,注意这里        s=forinput[i];        wh=mymap[s];        be=sccbelong[wh];        ans1+=vertarjan[be].numr;        ans2+=vertarjan[be].len;    }    printf("%lld %lld\n",ans1,ans2);}


0 0
原创粉丝点击