[USACO15JAN]草鉴定Grass Cownoisseur
tarjan缩点+最短路
这道题我想了一半的正解,就是缩点+DAG上考虑,之后图只有一种情况:1号点连着大量的点,大量的点连着1号点,部分能到达1号点的点连接着1号点能到达的部分点。转向就是要从1号点能到达的点过渡到能到达1号点的点。考虑spfa,在缩完点后的图上从1号点正向跑最大路,再建立一个反图,跑最大路。这样我们枚举每一个点(缩后的点),找到他连接的点,如果合法,那么ans=max(ans,-1*val[firs]+diss[i]+disf[v]);
code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
const int maxn=500006;
int x1,x2,y1,y2;
struct hzw
{
int to,next,u;
}e[maxn],ed1[maxn],ed2[maxn];
int head[maxn],head1[maxn],head2[maxn],cur,n,m,k;
inline void add(int a,int b,hzw e[],int head[])
{
e[cur].u=a;
e[cur].to=b;
e[cur].next=head[a];
head[a]=cur++;
}
stack<int>ss;
bool pan[maxn];
int cnt,col,val[maxn],firs,low[maxn],dfn[maxn],bel[maxn],disf[maxn],diss[maxn];
inline void tarjan(int s)
{
low[s]=++cnt;
dfn[s]=cnt;
ss.push(s);
for (int i=head[s];i!=-1;i=e[i].next)
{
int v=e[i].to;
if (!dfn[v])
{
tarjan(e[i].to);
low[s]=min(low[s],low[v]);
}
else if (!pan[v])
{
low[s]=min(low[s],dfn[v]);
}
}
if (low[s]==dfn[s])
{
col++;
while (ss.top()!=s)
{
int fr=ss.top();
ss.pop();
pan[fr]=1;
bel[fr]=col;
val[col]++;
if (fr==1) firs=col;
}
int fr=ss.top();
ss.pop();
pan[fr]=1;
bel[fr]=col;
val[col]++;
if (fr==1) firs=col;
}
}
bool vis[maxn];
void spfa(int s,hzw e[],int dis[],int *head){
memset (vis,0,sizeof (vis));
memset(dis,-1,sizeof(dis));
queue<int>q;
q.push(s);
dis[s]=val[firs];
vis[s]=true;
while (!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
for (int i=head[u];i!=-1;i=e[i].next){
int a=e[i].to;
if (dis[a]<dis[u]+val[a]){
dis[a]=dis[u]+val[a];
if (!(vis[a])){
q.push(a);
vis[a]=true;
}
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
cin>>n>>m;
for (int i=1,a,b;i<=m;++i)
{
scanf("%d%d",&a,&b);
add(a,b,e,head);
}
for (int i=1;i<=n;++i) if (!dfn[i]) {tarjan(i);}
for (int i=0;i<m;++i)
{
int x=bel[e[i].u],y=bel[e[i].to];
if (x!=y)
{
add(x,y,ed1,head1);
add(y,x,ed2,head2);
}
}
spfa(firs,ed1,disf,head1);
spfa(firs,ed2,diss,head2);
int ans=val[firs];
for (int i=1;i<=col;++i)
{
if (!diss[i]) continue;
for (int j=head1[i];j!=-1;j=ed1[j].next)
{
int v=ed1[j].to;
if (!disf[v]) continue;
ans=max(ans,-1*val[firs]+diss[i]+disf[v]);
}
}
cout<<ans;
return 0;
}
收获:
注意最长,最短路有时能解决常见的dp问题(有起点的那种),并且能够判断状态是否合法。