BZOJ 2788 Poi2012 Festival 差分约束+Tarjan+Floyd

题目大意:给定 n 个正整数变量和 m1+m2 个限制条件,每个形如 xi+1=yi xi<=yi ,求这些变量最多能有多少个不同的取值

首先我们可以根据差分约束建图,Floyd跑最短路,判断是否无解
然后Tarjan缩点,显然不同强连通分量之间互不影响
一个强连通分量内的最多取值个数等于强连通分量两两之间最短路的最大值 +1

证明:
由于边权只有 {0,1,1} 三种,因此取值数=最大值-最小值+1
不妨设最短路的最大值为 ans ,那么对于这个差分约束系统的任意一组解,我选择最小的数 x 和最大的数 y ,由于这个图强连通,因此 x y 必然存在至少一条路径
不妨设 x>y 的最短路径长度为 z ,那么取值数 1=yxzans
显然我们可以构造出一组解使得取值数 1=ans ,故ans就是最大的取值数

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 660
using namespace std;
int n,m1,m2,ans;
struct abcd{
    int to,next;
}table[200200];
int head[M],tot;
int f[M][M];
int dpt[M],low[M],T;
void Add(int x,int y)
{
    table[++tot].to=y;
    table[tot].next=head[x];
    head[x]=tot;
}
void Calculate(vector<int> &stack)
{
    int i,j,re=0;
    for(i=0;i<(signed)stack.size();i++)
        for(j=0;j<(signed)stack.size();j++)
            re=max(re,f[stack[i]][stack[j]]);
    ans+=re+1;
}
void Tarjan(int x)
{
    static int stack[M],top;
    static bool v[M];
    int i;
    dpt[x]=low[x]=++T;
    stack[++top]=x;
    for(i=head[x];i;i=table[i].next)
    {
        if(v[table[i].to])
            continue;
        if(dpt[table[i].to])
            low[x]=min(low[x],dpt[table[i].to]);
        else
            Tarjan(table[i].to),low[x]=min(low[x],low[table[i].to]);
    }
    if(dpt[x]==low[x])
    {
        int t;
        vector<int> *s=new vector<int>;
        do{
            t=stack[top--];
            v[t]=true;
            s->push_back(t);
        }while(t!=x);
        Calculate(*s);
    }
}
int main()
{
    int i,j,k,x,y;
    cin>>n>>m1>>m2;
    memset(f,0x3f,sizeof f);
    for(i=1;i<=m1;i++)
    {
        scanf("%d%d",&x,&y);
        Add(x,y);Add(y,x);
        f[x][y]=min(f[x][y],1);
        f[y][x]=min(f[y][x],-1);
    }
    for(i=1;i<=m2;i++)
    {
        scanf("%d%d",&x,&y);
        Add(y,x);
        f[y][x]=min(f[y][x],0);
    }
    for(i=1;i<=n;i++)
        f[i][i]=0;
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
    for(i=1;i<=n;i++)
        if(f[i][i]<0)
            return puts("NIE"),0;
    for(i=1;i<=n;i++)
        if(!dpt[i])
            Tarjan(i);
    cout<<ans<<endl;
    return 0;
}

你可能感兴趣的:(Tarjan,floyd,差分约束,bzoj,BZOJ2788)