P3530 [POI2012]FES-Festival


题目大意 对选手编号有些大小的约束条件 最后求不同编号的最大数目

首先可以想到差分约束 最后答案就是求最最短路+1(最开始的点)

因为缩完点之后各个点之间一定是0连接 所以两边大小可以随便取

判断负环只需要判断dis[i][i]<0 即可

#include

using namespace std;

inline int read()
{
    char ch=getchar();
    int f=1,x=0;
    while (ch<'0' || ch>'9')
    {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return f*x;
}

struct Edge {
    int next,to,w;
} edge[200005];
stack  s;
int scnt,dfsord,id[605],low[605],scc[605];
int cnt,head[605],n,m1,m2,a,b,dis[605][605],mx[605],ans;

inline void add(int u,int v,int w)
{
    edge[++cnt].to=v;
    edge[cnt].next=head[u];
    edge[cnt].w=w;
    head[u]=cnt;
}

void dfs(int x) //Tarjan
{
    s.emplace(x);
    id[x]=low[x]=++dfsord;
    for (int i=head[x];i;i=edge[i].next)
    {
        int y=edge[i].to;
        if (!id[y])
        {
            dfs(y);
            low[x]=min(low[x],low[y]);
        }
        else if (!scc[y]) low[x]=min(low[x],id[y]);
    }
    if (id[x]==low[x])
    {
        scnt++;
        while (!s.empty())
        {
            int t=s.top(); s.pop();
            scc[t]=scnt;
            if (t==x) break;
        }
    }
}

signed main()
{
    n=read(); m1=read(); m2=read();
    memset(dis,0x3f,sizeof(dis));
    for (int i=1;i<=n;i++) dis[i][i]=0;
    for (int i=1;i<=m1;i++)
    {
        a=read(); b=read();
        add(a,b,-1); add(b,a,1);
        dis[a][b]=min(dis[a][b],-1); dis[b][a]=min(dis[b][a],1); //取
    }
    for (int i=1;i<=m2;i++)
    {
        a=read(); b=read();
        add(a,b,0);
        dis[a][b]=min(dis[a][b],0);
    }
    for (int i=1;i<=n;i++)
    {
        if (id[i]) continue;
        dfs(i);
    }
    for (int k=1;k<=n;k++)
    {
        for (int i=1;i<=n;i++)
        {
            if (scc[i]!=scc[k]) continue;
            for (int j=1;j<=n;j++)
            {
                if (scc[j]!=scc[i]) continue;
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); //对每个强连通分量求出最短路
            }
        }
    }
    for (int i=1;i<=n;i++) if (dis[i][i]) return 0&puts("NIE"); //判负环
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            if (scc[i]!=scc[j]) continue;
            mx[scc[i]]=max(mx[scc[i]],dis[i][j]); //强连通分量
        }
    }
    for (int i=1;i<=scnt;i++) ans+=mx[i];
    return !printf("%d",ans+scnt);
}