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

题意:链接

方法:差分约束+tarjan+floyd

解析:

说实话蛮简单的题。

不过考试的时候差分约束都忘光了,连完边就不到咋搞了=-=,于是强行spfa求奇怪的东西骗分来着。

然并卵

首先我们能建出差分约束系统。

然后我们考虑对于每个强连通分量。

内部的答案依据差分约束显然是最长路+1.

但是如果两个相连接的强连通分量怎么办呢。

依据题中的要求,xc<=xd,所以我们可以想像,相连接的强连通分量一定是由0边连接的。所以我们可以极限考虑一下,即一个强连通分量里的值无限小,另一个无限大(无限大な梦のあとの 何もない世の中じゃ),也就是说,二者的取值是不影响的。

所以我们只需要求出每个强连通分量内的答案,最后求和即可。

任意两点最长路对于n<=600肯定会考虑floyd,不要问我为什么复杂度允许,剪枝很多的话复杂度是玄学。

另外,如果出现正权环的话就是NIE。

如何判断正权环呢?

初始化map[i][i]=0,跑完floyd后看是否还是0即可。

复杂度 O(玄学),最坏O(n^3)不虚。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 610
#define M 200010
#define INF 0x3f3f3f3f
using namespace std;
int n,m1,m2,cnt,tot,top,num;
int head[N];
int deep[N],low[N],sta[N],ins[N],v[N],belong[N];
int map[N][N],vw[N][N];
struct node
{
    int from,to,val,next;
}edge[M];
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void edgeadd(int from,int to,int val)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].val=val;
    edge[cnt].next=head[from],head[from]=cnt++;
}
void tarjan(int now)
{
    deep[now]=low[now]=++tot;
    ins[now]=1,sta[++top]=now;
    for(int i=head[now];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(!deep[to])
        {
            tarjan(to);
            low[now]=min(low[now],low[to]);
        }else if(ins[to])low[now]=min(low[now],deep[to]);
    }
    if(deep[now]==low[now])
    {
        int t;
        num++;
        do
        {
            t=sta[top--];
            belong[t]=num;
            ins[t]=0;
        }while(t!=now);
    }
}
int main()
{
    init();
    scanf("%d%d%d",&n,&m1,&m2);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            map[i][j]=-INF;
    for(int i=1;i<=n;i++)map[i][i]=0; 
    for(int i=1;i<=m1;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        edgeadd(x,y,1);
        edgeadd(y,x,-1);
        map[x][y]=max(map[x][y],1),map[y][x]=max(map[y][x],-1);
    }
    for(int i=1;i<=m2;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        edgeadd(x,y,0);
        map[x][y]=max(map[x][y],0);
    }
    for(int i=1;i<=n;i++)
    {
        if(!deep[i])tarjan(i);
    }
    int ans=0;
    for(int b=1;b<=num;b++)
    {
        int ret=0;
        for(int k=1;k<=n;k++)
        {
            if(belong[k]!=b)continue;
            for(int i=1;i<=n;i++)
            {
                if(belong[i]!=b)continue;
                if(map[i][k]==-INF)continue;
                for(int j=1;j<=n;j++)
                {
                    if(belong[j]!=b)continue;
                    if(map[k][j]==-INF)continue;
                    map[i][j]=max(map[i][j],map[i][k]+map[k][j]);
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(belong[i]!=b)continue;
            for(int j=1;j<=n;j++)
            {
                if(belong[j]!=b)continue;
                ret=max(ret,abs(map[i][j]));
            }
        }
        ans+=ret+1;
    }
    for(int i=1;i<=n;i++)if(map[i][i]!=0){puts("NIE");return 0;}
    printf("%d\n",ans);
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值