[Poi2012]Festival

问题 D: [Poi2012]Festival

时间限制: 1 Sec  内存限制: 64 MB

题目描述

有n个正整数X1,X2,...,Xn,再给出m1+m2个限制条件,限制分为两类:

1. 给出a,b (1<=a,b<=n),要求满足Xa + 1 = Xb

2. 给出c,d (1<=c,d<=n),要求满足Xc <= Xd

在满足所有限制的条件下,求集合{Xi}大小的最大值。

输入

第一行三个正整数n, m1, m2 (2<=n<=600, 1<=m1+m2<=100,000)。

接下来m1行每行两个正整数a,b (1<=a,b<=n),表示第一类限制。

接下来m2行每行两个正整数c,d (1<=c,d<=n),表示第二类限制。

输出

一个正整数,表示集合{Xi}大小的最大值。

如果无解输出NIE。

样例输入

4 2 21 23 41 43 1

样例输出

3

提示

|X3=1, X1=X4=2, X2=3

这样答案为3。容易发现没有更大的方案。

solution

    首先想到差分约束。对于限制2,Xc<=Xd,我们可以把它导成Xc-Xd<=0,由d向c建一条边权为0的边。对于限制1,我们可以转换成Xa+1<=Xb Xa+1>=Xb,然后再导成Xa-Xb<=-1 Xb-Xa<=1,由b向a建一条边权为-1的边,由a向b建一条边权为1的边。注意如果之前已经在两点之间建过边,边权取最小值。

    然后我们开始在图上跑。由于需要满足条件,我们跑最短路。先用tarjan缩点,在每一个环里跑两点之间距离的最小值。如果跑出来发现dis[i][i]不为0,说明不能满足条件。如果能满足,取每个环里两点之间距离绝对值的最大值,相加即为集合最大的大小。

附上代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct tree{
    int u,v,next,d;
}l[301000];
int n,m1,m2,lian[605],e,dis[605][605];
int low[605],dfn[605],num,fa[605],cnt,head,st[605];
bool pd[605];
void bian(int,int,int);
void tarjan(int);
int mmin(int x,int y)
{
    if(x>y) return y;
    return x;
}
int mmax(int x,int y)
{
    if(x>y) return x;
    return y;
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d%d%d",&n,&m1,&m2);
    memset(dis,30,sizeof(dis));
    for(int i=1;i<=m1;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        bian(y,x,-1);
        bian(x,y,1);
        dis[y][x]=mmin(dis[y][x],-1);
        dis[x][y]=mmin(dis[x][y],1);
    }
    for(int i=1;i<=m2;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        bian(y,x,0);
        dis[y][x]=mmin(dis[y][x],0);
    }
    for(int i=1;i<=n;i++)
        if(dfn[i]==0)
            tarjan(i);
    for(int i=1;i<=n;i++)
        dis[i][i]=0;
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=n;j++)
        {
            if(fa[j]!=i) continue;
            for(int k=1;k<=n;k++)
            {
                if(fa[k]!=i) continue;
                for(int l=1;l<=n;l++)
                {
                    if(fa[l]!=i) continue;
                    dis[k][l]=mmin(dis[k][l],dis[k][j]+dis[j][l]);
                }
            }
        }
    bool cun=0;
    for(int i=1;i<=n;i++)
        if(dis[i][i]!=0)
        {
            cun=1;break;
        }
    if(cun==1)
    {
        printf("NIE");
        return 0;
    }
    else
    {
        int zui=0;
        for(int i=1;i<=cnt;i++)
        {
            int mx=0;
            for(int j=1;j<=n;j++)
            {
                if(fa[j]!=i) continue;
                for(int k=1;k<=n;k++)
                {
                    if(fa[k]!=i) continue;
                    mx=mmax(abs(dis[j][k]),mx);
                }
            }
            zui+=mx;
            ++zui;
        }
        printf("%d",zui);
    }
    return 0;
}
void bian(int x,int y,int z)
{
    e++;
    l[e].u=x;
    l[e].v=y;
    l[e].d=z;
    l[e].next=lian[x];
    lian[x]=e;
}
void tarjan(int x)
{
    ++num;
    dfn[x]=num;
    low[x]=num;
    pd[x]=1;
    st[++head]=x;
    for(int i=lian[x];i;i=l[i].next)
    {
        int v=l[i].v;
        if(dfn[v]==0)
        {
            tarjan(v);
            low[x]=mmin(low[x],low[v]);
        }
        else
            if(pd[v]==1)
                low[x]=mmin(dfn[v],low[x]);
    }
    if(dfn[x]==low[x])
    {
        cnt++;
        while(1)
        {
            int tmp=st[head];head--;        
            fa[tmp]=cnt;
            pd[tmp]=0;
            if(tmp==x) break;
        }
    }
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值