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

14 篇文章 0 订阅
8 篇文章 0 订阅

题目大意:给定 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;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值