[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问题(有起点的那种),并且能够判断状态是否合法。

posted @ 2018-09-21 21:29  Splitor  阅读(181)  评论(0编辑  收藏  举报