[POI2012]FES-Festival

题意

对于一些数字,有两种限制;1:a+1=b;2:a<=b;

求这些数字最多有多少不同数值。

对于100%的数字n<=600,限制<=1e5

题解

可以得到将限制转化:1:b<=a+1<=b-->b<=a+1,a<=b-1

2:a<=b+0

那么可以建出边,跑最短路;

找负环时,如果用bfs版spfa会发现出发点可能和负环不连通。

于是先tarjan找出联通块,建虚拟源点连向每个联通块,再跑spfa。

注意到联通块之间只用边权为0的边连接,这些边是由第二个限制产生,即a<=b,但这并不确定绝对大小,所以对于b所在联通块可以无限增大值,这样就可以使得两者的值无交集,那么就可以使得有更多取值。

对于同一联通块,里面的边只有1或-1,所以里面的取值是连续的,所以取值数量就是最大的差值+1.

这里主要想记一种floyed求负环;

秩序在跑出floyed后判断f[i][i]是否为0,设初值为0.

在floyed时,判断是否在同一联通块和是否有边可以做到强力剪枝,快到飞起。

#include<bits/stdc++.h>
using namespace std;

const int maxn=605;
const int maxm=100005;
int n,m1,m2;
int mx[maxn],ans;
int cur,cnt,dfn[maxn],low[maxn];
int top,sta[maxn],res[maxn];
bool tag[maxn];
int dis[maxn][maxn];
vector<pair<int,int> >e[maxn];

void tarjan(int u){
    low[u]=dfn[u]=++cur;
    sta[++top]=u;
    tag[u]=true;
    
    for(unsigned int i=0;i<e[u].size();i++){
      int v=e[u][i].first;
      if(!dfn[v]){
        tarjan(v);
        low[u]=min(low[u],low[v]);
      }
      else if(tag[v]) low[u]=min(low[u],dfn[v]);
    }
    
    if(dfn[u]==low[u]){
      cnt++;
      int t;
      do{
          t=sta[top--];
          res[t]=cnt;
          tag[t]=false;
      }while(t!=u);
    }
}

int main(){
    scanf("%d%d%d",&n,&m1,&m2);
    
    memset(dis,0x3f3f,sizeof(dis));
    for(int i=1;i<=n;i++) dis[i][i]=0;
    
    for(int i=1;i<=m1;i++){//a+1=b;
        int a,b;
        scanf("%d%d",&a,&b);
        //b<=a+1<=b
        e[a].push_back(make_pair(b,1));
        e[b].push_back(make_pair(a,-1));
        dis[a][b]=min(dis[a][b],1);
        dis[b][a]=min(dis[b][a],-1);
    }
    
    for(int i=1;i<=m2;i++){//a<=b
        int a,b;
        scanf("%d%d",&a,&b);
        e[b].push_back(make_pair(a,0));
        dis[b][a]=min(dis[b][a],0);
    }
    
    for(int i=1;i<=n;i++)
     if(!dfn[i]) tarjan(i);
     
    for(int k=1;k<=n;k++)
     for(int i=1;i<=n;i++)
      if(res[i]==res[k]&&dis[i][k]!=dis[0][0])//第二句剪枝快了不知道多少倍 
       for(int j=1;j<=n;j++)
        if(res[i]==res[j]&&dis[k][j]!=dis[0][0])
        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        
    for(int i=1;i<=n;i++) if(dis[i][i]) {printf("NIE");exit(0);}
    
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      if(res[i]==res[j])
       mx[res[i]]=max(mx[res[i]],dis[i][j]);
       
    for(int i=1;i<=cnt;i++) ans+=mx[i]+1;
    printf("%d",ans);
}
View Code
原文地址:https://www.cnblogs.com/sto324/p/11228144.html