1. 程式人生 > >[USACO15JAN]草鑒定Grass Cownoisseur

[USACO15JAN]草鑒定Grass Cownoisseur

usaco 部分 %d using pop pty memset tarjan ems

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問題(有起點的那種),並且能夠判斷狀態是否合法。

[USACO15JAN]草鑒定Grass Cownoisseur