不難看出,這是一道圖論的題,只要要求在\(r\),的個數最小時,\(r\)的個數與文章長度。
預備知識
- STL之map (內置應該是hash之類的)
- tarjan縮點
- 樹形dp
- 簡單字符串
- 鄰接表存邊
問題分析
由於同義是單向的,我們建起了單向邊,容易的是,如果一個單詞可以最后回到他自己,那就把這個環上的點縮成一個scc,記下每個scc的最優\(r\)和最優\(leg\)(即\(length\)但我由於個人原因更喜歡用\(lgh\))
在對我們所得的每一個強連通,進行重構圖。最后跑一個\(dp\)即可(可以用深搜實現)
統計答案時,對每一個word獨立操作即可
如果你還是不太清楚,我們再來看圖(樣例一)
我們先建一個對應關系
然后,我們間的圖即為
然后,我們先進行縮點,然后統計出每個強連通的最優值,最后跑一遍樹形dp就可以了
其實只要看清楚這個題的意思,就很好AC了
時間復雜度分析
- tarjan O(n+m)
- 統計最優值 O(n)
- 樹形dp O(n)
好的沒有毒瘤\(n^2\) ,此題可過
附上代碼
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <string>
#include <map>
using namespace std;
const int Maxn=1e5+1;
map<string,int> wd2;
int num,n,m,cnt,leg[Maxn],r[Maxn],h[Maxn],vcnt,col[Maxn],dfn[Maxn],low[Maxn],dep,sta[Maxn],top;
bool fsta[Maxn],flag[Maxn];
long long ans1,ans2;
string word[Maxn],str1,str2;
struct Edge{
int fr,to,lac;
}edge[Maxn];
struct Node{
int rmin,legmin;
}scc[Maxn];
int read(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch<='9'&&ch>='0'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x;
}
char me(char ch){
if(ch>='A'&&ch<='Z') ch+=32;
return ch;
}
void insert(int x,int y){
edge[vcnt].fr=x;
edge[vcnt].to=y;
edge[vcnt].lac=h[x];
h[x]=vcnt++;
}
string Getstr(){
string str;
char ch=getchar();
while(!((ch>='A'&&ch<='Z')||(ch>='a'&&ch<='z'))){
ch=getchar();
}
while((ch>='A'&&ch<='Z')||(ch>='a'&&ch<='z')){
str+=me(ch);
ch=getchar();
}
return str;
}
void make(string str){
leg[cnt]=str.size();
for(int i=0;i<=str.size();i++){
if(str[i]=='r') r[cnt]++;
}
return ;
}
void sol(string str){
if(wd2[str]==0){
wd2[str]=++cnt;
make(str);
}
}
void tarjan(int u){
dfn[u]=low[u]=++dep;
sta[++top]=u;fsta[u]=1;
for(int i=h[u];i!=-1;i=edge[i].lac){
int to=edge[i].to;
if(dfn[to]){
if(fsta[to]) low[u]=min(low[u],dfn[to]);
continue;
}
tarjan(to);
low[u]=min(low[u],low[to]);
}
if(low[u]==dfn[u]){
num++;
while(fsta[u]){
fsta[sta[top]]=0;
col[sta[top--]]=num;
}
}
return ;
}
void dfs(int u){
flag[u]=1;
for(int i=h[u];i!=-1;i=edge[i].lac){
int to=edge[i].to;
if(!flag[to]) dfs(to);
if(scc[to].rmin<=scc[u].rmin){
if(scc[to].rmin<scc[u].rmin) scc[u].legmin=scc[to].legmin;
else scc[u].legmin=min(scc[u].legmin,scc[to].legmin);
scc[u].rmin=scc[to].rmin;
}
}
return ;
}
int main() {
// wd1 int-> str 每個str的號->對應str
// wd2 str-> int 每個strstr對應 號
// freopen("puditan.in","r",stdin);
m=read();
for(int i=1;i<=m;i++) {
word[i]=Getstr();
sol(word[i]);
}
memset(h,-1,sizeof h);
n=read();
for(int i=1;i<=n;i++){
str1=Getstr(),str2=Getstr();
sol(str1);sol(str2);
insert(wd2[str1],wd2[str2]);
}
for(int i=1;i<=cnt;i++)
if(!dfn[i])
tarjan(i);//cnt指節點個數
for(int i=1;i<=num;i++) scc[i].legmin=0x3f3f3f3f,scc[i].rmin=0x3f3f3f3f;//num是強連通
for(int i=1;i<=cnt;i++)
if(scc[col[i]].rmin>=r[i]){
if(scc[col[i]].rmin>r[i]) scc[col[i]].legmin=leg[i];
else scc[col[i]].legmin=min(scc[col[i]].legmin,leg[i]);
scc[col[i]].rmin=r[i];
}
int q=vcnt;
vcnt=0;
memset(h,-1,sizeof h);
for(int i=0;i<q;i++){
int to=edge[i].to,fr=edge[i].fr;
if(col[to]==col[fr]) continue;
insert(col[fr],col[to]);
}
for(int i=1;i<=num;i++) if(!flag[i]) dfs(i);
for(int i=1;i<=m;i++){
ans1+=scc[col[wd2[word[i]]]].rmin;
ans2+=scc[col[wd2[word[i]]]].legmin;
}
printf("%lld %lld",ans1,ans2);
return 0;
}
感想 ,這道題拖了很久沒做,還是內心的懼怕呀