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

Posted clover_hxy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2788: [Poi2012]Festival (差分约束+最短路+tarjan)相关的知识,希望对你有一定的参考价值。

题目描述

传送门

题目大意:有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);

以上是关于bzoj 2788: [Poi2012]Festival (差分约束+最短路+tarjan)的主要内容,如果未能解决你的问题,请参考以下文章

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

bzoj2788 festival 差分约束

[BZOJ2792][Poi2012]Well

[BZOJ2797][Poi2012]Squarks

[BZOJ2799][Poi2012]Salaries

[BZOJ2791][Poi2012]Rendezvous