bzoj 2788: [Poi2012]Festival (差分约束+最短路+tarjan)

24 篇文章 0 订阅
17 篇文章 0 订阅

题目描述

传送门

题目大意:有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}大小的最大值。

题解

首先按照给出的条件建出最短路图,然后用spfa判断是否存在负环,如果存在那么就是输出无解。
然后对图做tarjan,将强联通分量缩到一起。可以发现连接两个不同的强联通分量的边一定是第二种边,也就是 Xc<=Xd 的形式,那么这个形式中的 Xd 的取值是无限大,也就是说不同的强联通分量中的点取值一定可以是不冲突的。
一个强连通分量内的最多取值个数等于强连通分量两两之间最短路的最大值+1
考虑一个强联通分量中的点,边权只有{0,1,-1}三种取值,取值数=最大值-最小值+1.
设最短路的最大值为mx,那么对于这个差分约束系统的任意一组解,我选择最小的数x和最大的数y,由于这个图强连通,因此x到y必然存在至少一条路径
不妨设 x>y 的最短路径长度为z,那么取值数 1=yxzmx
所以一定可以构造出一组解使得取值数−1=mx,故mx+1就是最大的取值数
证明参考自popoqqq

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 500003
#define inf 1000000000
using namespace std;
int tot,nxt[N],point[N],v[N],c[N],dis[N],can[N],mp[N];
int n,m1,m2,ans[N];
int cnt,sz,dfsn[N],low[N],size[N],belong[N],ins[N],cnt1[N],st[N],top;
bool pd=false;
struct data{
    int x,y,val,opt;
}e[N],p[N];
void add(int x,int y,int z)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
//  cout<<x<<" "<<y<<" "<<z<<endl;
}
struct node{
    int dis[603][603],n;
    void clear(){
        for (int i=1;i<=n;i++)
         for (int j=1;j<=n;j++) dis[i][j]=inf;
    }
    void add(int x,int y,int z){
        dis[x][y]=min(dis[x][y],z);
    }
    void floyed()
    {
        for (int k=1;k<=n;k++)
         for (int i=1;i<=n;i++)
          for (int j=1;j<=n;j++){
            if (i==j||k==i||j==k) continue;
            if (dis[i][k]==inf||dis[k][j]==inf) continue;
            if(dis[i][j]>dis[i][k]+dis[k][j])
             dis[i][j]=dis[i][k]+dis[k][j];
          }
        for (int i=1;i<=n;i++)
         for (int j=1;j<=n;j++) 
          if (dis[i][j]!=inf) {
            int r1=belong[i]; int r2=belong[j];
            if(r1==r2) 
             ans[r1]=max(ans[r1],dis[i][j]);
          }
    }
}T;
void spfa(int x)
{
    can[x]=1;
    for (int i=point[x];i;i=nxt[i])
     if (dis[v[i]]>dis[x]+c[i]) {
        dis[v[i]]=dis[x]+c[i];
        if (can[v[i]]) {
            pd=true;
            return;
         }
        spfa(v[i]);
        if (pd) return;
     }
    can[x]=0;
}
void tarjan(int x)
{
    dfsn[x]=low[x]=++sz; ins[x]=1; st[++top]=x;
    for (int i=point[x];i;i=nxt[i]){
        if (!dfsn[v[i]]) {
            tarjan(v[i]);
            low[x]=min(low[x],low[v[i]]);
        }
        else if (ins[v[i]]) low[x]=min(low[x],dfsn[v[i]]);
    }
    if (low[x]==dfsn[x]) {
        int j; ++cnt;
        do{
           j=st[top--];
           belong[j]=cnt;
           size[cnt]++;
           ins[j]=0;
        }while (j!=x);
    }
}
int cmp(data a,data b)
{
    return a.opt<b.opt;
}
int main()
{
    freopen("a.in","r",stdin);
//  freopen("my.out","w",stdout);
    scanf("%d%d%d",&n,&m1,&m2);
    for (int i=1;i<=m1;i++) {
        int x,y; scanf("%d%d",&x,&y);
        add(y,x,-1); add(x,y,1);
    }
    for (int i=1;i<=m2;i++) {
        int x,y; scanf("%d%d",&x,&y);
        add(y,x,0);
    }
    memset(dis,127/3,sizeof(dis));
    for (int i=1;i<=n;i++)
     if (!can[i]) {
        dis[i]=0; spfa(i);
        if(pd) break;
     }
    if (pd) {
        printf("NIE\n");
        return 0;
    }
    for(int i=1;i<=n;i++)
     if (!dfsn[i]) tarjan(i);
    T.n=n; T.clear();
    for (int i=1;i<=n;i++)
     for (int j=point[i];j;j=nxt[j]){
        int r1=belong[i]; int r2=belong[v[j]];
        if (r1==r2) T.add(i,v[j],c[j]);
     }
    T.floyed(); int sum=0;
    for (int i=1;i<=cnt;i++) sum+=ans[i]+1;
    printf("%d\n",sum);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值