bzoj 2788 [Poi2012]Festival tarjan+floyd+差分约束

本文介绍了一种利用差分约束系统求解特定图论问题的方法。通过Floyd算法检测负环来判断问题是否有解,并使用Tarjan算法进行强连通分量压缩,最终求得每个强连通分量内的点间最长路径的最大值,从而得到问题的整体最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先把这个东西建成差分约束。
跑一遍floyd,如果有负环一定无解。
用tarjan缩点,由于缩完点是一个拓扑图,所以每个强连通分量中的取值是互不影响的(因为可以把拓扑序大的强连通分量中的值无限减少)。所以可以将每个强连通分量的答案相加。

然后对于一个强连通分量的答案是这个强连通分量中的点两两之间最长路的最大值+1。
因为所有权值都是0,1,-1,因此从最小值到最大值的所有值都会取到。由于从最小值到最大值一定存在一条路径,因此答案最大为这个。
又由于差分约束如果有解那么一个环中的一段路径(不为整个环)一定可以满载,因此这是可以取到的。

#include <bits/stdc++.h>
using namespace std;
#define N 810
#define M 210000
int n,m1,m2,tot,cnt,top,scc,ans;
int mp[N][N],head[N],nex[M],to[M];
int deep[N],low[N],st[N],ins[N],bel[N];
void add(int x,int y)
{
    nex[++tot]=head[x];head[x]=tot;
    to[tot]=y;
}
void tarjan(int x)
{
    deep[x]=low[x]=++cnt;
    st[++top]=x;ins[x]=1;
    for(int i=head[x];i;i=nex[i])
    {
        if(!deep[to[i]])
            tarjan(to[i]),low[x]=min(low[x],low[to[i]]);
        else if(ins[to[i]])
            low[x]=min(low[x],deep[to[i]]);
    }
    if(deep[x]==low[x])
    {
        int tmp=st[top--];
        bel[tmp]=++scc;ins[tmp]=0;
        while(tmp!=x)
        {
            bel[tmp=st[top--]]=scc;
            ins[tmp]=0;
        }
    }
}
int main()
{
    //freopen("tt.in","r",stdin);
    memset(mp,0x3f,sizeof(mp));
    scanf("%d%d%d",&n,&m1,&m2);
    for(int i=1;i<=n;i++)mp[i][i]=0;
    for(int i=1,x,y;i<=m1;i++)
    {
        scanf("%d%d",&x,&y);
        mp[x][y]=min(mp[x][y],1);
        mp[y][x]=min(mp[y][x],-1);
        add(x,y);add(y,x);
    }
    for(int i=1,x,y;i<=m2;i++)
    {
        scanf("%d%d",&x,&y);
        mp[y][x]=min(mp[y][x],0);
        add(y,x);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                mp[j][k]=min(mp[j][k],mp[j][i]+mp[i][k]);
    for(int i=1;i<=n;i++)
        if(mp[i][i]<0)return puts("NIE"),0;
    for(int i=1;i<=n;i++)
        if(!deep[i])tarjan(i);
    for(int i=1;i<=scc;i++)
    {
        int mn=0;
        for(int j=1;j<=n;j++)
            if(bel[j]==i)
                for(int k=1;k<=n;k++)
                    if(bel[k]==i)
                        mn=max(mn,mp[j][k]);
        ans+=mn+1;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值