267D.Fedor and Essay(强连通分量缩点+DAG上DP)
在帮助Fedor在“ Call of Soldiers 3”游戏中找到朋友之后,他完全停止了学习。今天,英语老师告诉他准备一篇论文。费多(Fedor)不想准备这篇论文,因此他向亚历克斯(Alex)寻求帮助。亚历克斯来帮忙,为费多写了文章。但是费多尔根本不喜欢这篇论文。现在,Fedor将使用英语的同义词词典来更改论文。
Fedor不想改变论文的意思。因此,他唯一要做的更改是:根据词典中的替换规则,将单词从论文更改为同义词之一。 Fedor可以执行多次此操作。
结果,Fedor希望获得一篇论文,其中包含尽可能少的字母“ R”(大小写无关紧要)。如果有多篇论文的“ R”个数最少,他希望得到一篇长度最小的文章(论文的长度是其中所有单词的长度之和)。帮助Fedor获得所需的论文。
请注意,在此问题中字母的大小写无关紧要。例如,如果同义词词典说单词cat可以用单词DOG代替,则可以用单词doG代替单词Cat。
题解:
考虑到同义词转换关系是单向的,这个问题可以看成是一个有向图的模型。
考虑先对有向图缩点,形成若干个DAG树,每个点代表一个强连通分量,并保存这个强连通分量里面的最优解。然后在每个DAG上DP,得到母串中每个单词的最优解即可。
调了好久,写的很乱。
//使R的数量最少 //对于同一个单词 //查询它的所有同义词 //每对同义词是一条单向边 //那么建立完成后可以得到若干个强连通分量树 //在强连通分量树上做树形dp,每个强连通分量缩点 //缩点后每组同义词里优先取r最少的,即统计每个强连通分量的最优解 //如果有并列,取更短的 //然后会形成若干个DAG,在DAG上做一个dp,不断取最优解,最后取每个DAG终点的最优解总和即可 #include<bits/stdc++.h> using namespace std; const int maxn=3e5+100; int n,m; map<string,int> mp1; map<int,string> mp2; string t[maxn]; int tot; int f[maxn];//含有母串的强连通分量标记 int cnt,scc,low[maxn],dfn[maxn],pos[maxn]; string ans[maxn];//保存每个强连通分量的最优解 stack<int> st; vector<int> g[maxn],G[maxn]; void tj (int x) { low[x]=dfn[x]=++cnt; st.push(x); for (int y:g[x]) { if (!low[y]) { tj(y); low[x]=min(low[x],low[y]); } else if (!pos[y]) { low[x]=min(low[x],dfn[y]); } } if (low[x]==dfn[x]) { scc++; while (1) { int u=st.top(); st.pop(); low[u]=low[x]; pos[u]=scc; if (u==x) break; } } } int x[maxn],y[maxn]; int A[maxn],B[maxn]; int vis[maxn]; long long ans1=0,ans2=0; void dfs (int u) { //在DAG图上DP //每个单词可以转化成别的单词 vis[u]=1; for (int v:G[u]) { if (!vis[v]) dfs(v); if (A[v]<A[u]||(A[v]==A[u]&&B[v]<B[u])) A[u]=A[v],B[u]=B[v],ans[u]=ans[v]; } } int main () { scanf("%d",&n); for (int i=1;i<=n;i++) cin>>t[i]; for (int i=1;i<=n;i++) { for (int j=0;j<t[i].size();j++) { if (t[i][j]>='A'&&t[i][j]<='Z') t[i][j]=t[i][j]-'A'+'a'; } if (!mp1[t[i]]) { mp1[t[i]]=++tot; mp2[tot]=t[i]; } } scanf("%d",&m); for (int i=1;i<=m;i++) { string s1,s2; cin>>s1>>s2; for (int j=0;j<s1.size();j++) { if (s1[j]>='A'&&s1[j]<='Z') s1[j]=s1[j]-'A'+'a'; } for (int j=0;j<s2.size();j++) { if (s2[j]>='A'&&s2[j]<='Z') s2[j]=s2[j]-'A'+'a'; } if (!mp1[s1]) { mp1[s1]=++tot; mp2[tot]=s1; } if (!mp1[s2]) { mp1[s2]=++tot; mp2[tot]=s2; } g[mp1[s1]].push_back(mp1[s2]);//建图 x[i]=mp1[s1]; y[i]=mp1[s2]; } for (int i=1;i<=tot;i++) if (!low[i]) tj(i); for (int i=1;i<=n;i++) f[pos[mp1[t[i]]]]++;//标记母串单词所在的强连通分量 for (int i=1;i<=tot;i++) { if (ans[pos[i]]=="") { ans[pos[i]]=mp2[i]; continue; } int cnt1=0,cnt2=0; for (int j=0;j<ans[pos[i]].size();j++) cnt1+=(ans[pos[i]][j]=='r'); for (int j=0;j<mp2[i].size();j++) cnt2+=(mp2[i][j]=='r'); if (cnt1>cnt2) ans[pos[i]]=mp2[i]; else if (cnt1==cnt2&&ans[pos[i]].size()>mp2[i].size()) ans[pos[i]]=mp2[i]; } set<pair<int,int> > st; for (int i=1;i<=m;i++) { if (pos[x[i]]==pos[y[i]]) continue; st.insert(make_pair(pos[x[i]],pos[y[i]])); //对边去重 } for (auto it=st.begin();it!=st.end();it++) { G[(*it).first].push_back((*it).second); } //缩点后应该是若干个DAG图 //在DAG图上DP //每个单词背后是一条同义词链 //我们需要从这个链里面选择一个最优解 //在DAG图上DFS //为每个单词确定一个最优解 for (int i=1;i<=scc;i++) { for (int j=0;j<ans[i].size();j++) A[i]+=(ans[i][j]=='r'); B[i]+=ans[i].size(); } for (int i=1;i<=n;i++) { if (!vis[pos[mp1[t[i]]]]) dfs(pos[mp1[t[i]]]); } for (int i=1;i<=n;i++) ans1+=A[pos[mp1[t[i]]]],ans2+=B[pos[mp1[t[i]]]]; printf("%lld %lld\n",ans1,ans2); }