题解 P3119 【[USACO15JAN]草鉴定Grass Cownoisseur】
此题难在建模。
看到这道题,首先想到的是
简单概述一下
由于这个
然后便是在
考虑一张图,将这个图复制一份,点的编号从
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
const int maxn=100010;
const int inf=2e9;
vector<int>G[maxn];
vector<int>G1[maxn*2];
int n,m,u,v,op,index_,dfn[maxn],low[maxn],bcc[maxn],siz[maxn*2],cnt,dist[maxn*2];
bool tf[maxn*2];
stack<int>st;
queue<int>q;
void dfs(int cur)
{
dfn[cur]=low[cur]=++index_;
tf[cur]=true;
st.push(cur);
for(int i=0;i<G[cur].size();i++)
{
if(!dfn[G[cur][i]])
{
dfs(G[cur][i]);
low[cur]=min(low[cur],low[G[cur][i]]);
}
else if(tf[G[cur][i]])low[cur]=min(low[cur],dfn[G[cur][i]]);
}
if(dfn[cur]==low[cur])
{
cnt++;
while(st.top()!=cur)
{
bcc[st.top()]=cnt;
tf[st.top()]=false;
siz[cnt]++;
st.pop();
}
bcc[st.top()]=cnt;
tf[st.top()]=false;
siz[cnt]++;
st.pop();
}
}
int main()
{
scanf("%d%d",&n,&m);
while(m--)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
}
for(int i=1;i<=n;i++)if(!dfn[i])dfs(i);
for(int i=1;i<=cnt;i++)siz[cnt+i]=siz[i];
for(int i=1;i<=n;i++)for(int j=0;j<G[i].size();j++)if(bcc[i]!=bcc[G[i][j]])
{
G1[bcc[i]].push_back(bcc[G[i][j]]);
G1[bcc[G[i][j]]].push_back(bcc[i]+cnt);
G1[bcc[i]+cnt].push_back(bcc[G[i][j]]+cnt);
}
tf[bcc[1]]=true;q.push(bcc[1]);
while(!q.empty())
{
int x=q.front();
for(int i=0;i<G1[x].size();i++)if(dist[G1[x][i]]<dist[x]+siz[x])
{
dist[G1[x][i]]=dist[x]+siz[x];
if(!tf[G1[x][i]])tf[G1[x][i]]=true,q.push(G1[x][i]);
}
q.pop();tf[x]=false;
}
printf("%d\n",dist[bcc[1]+cnt]);
return 0;
}
用这样的思想,还可以解决一道这样的问题:给定一个有向图
感谢实哥大佬提供的
如果整个图是一个强连通分量,转换成新图的时候就不会加边,所以最后节点的最长路是错误的。
解决方法也显而易见,就是建新图的时候从起点直接往终点连一条边权为起点代表的强连通分量的大小的边。
不贴代码了(其实只用改一行)