别怪我=。=
我本来是写好了题解的,结果按错一个键,题解没有保存下来。
懒,不写了。总的来说方法就是标题所列,那也是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 M
int 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);
}