CF467D. Fedor and Essay


不難看出,這是一道圖論的題,只要要求在\(r\),的個數最小時,\(r\)的個數與文章長度。

預備知識

  • STL之map (內置應該是hash之類的)
  • tarjan縮點
  • 樹形dp
  • 簡單字符串
  • 鄰接表存邊

問題分析

由於同義是單向的,我們建起了單向邊,容易的是,如果一個單詞可以最后回到他自己,那就把這個環上的點縮成一個scc,記下每個scc的最優\(r\)和最優\(leg\)(即\(length\)但我由於個人原因更喜歡用\(lgh\))

在對我們所得的每一個強連通,進行重構圖。最后跑一個\(dp\)即可(可以用深搜實現)

統計答案時,對每一個word獨立操作即可

如果你還是不太清楚,我們再來看圖(樣例一)

我們先建一個對應關系

然后,我們間的圖即為

然后,我們先進行縮點,然后統計出每個強連通的最優值,最后跑一遍樹形dp就可以了

其實只要看清楚這個題的意思,就很好AC了

時間復雜度分析

  1. tarjan O(n+m)
  2. 統計最優值 O(n)
  3. 樹形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;
}

感想 ,這道題拖了很久沒做,還是內心的懼怕呀


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM