【 [POI2012]FES-Festival(NKOJ party)】差分约束+tarjan+floyd

想了几个小时无果,真是太弱了orz orz orz 这道题主要是平时总是差分约束和spfa结合,少有来一道差分约束搭配tarjan强联通分量的题,就跪了,说明差分约束还是没要搞透彻额呃呃。 BZOJ2788(权限) Luo3530

2788: [Poi2012]Festival

Time Limit: 30 Sec  Memory Limit: 64 MB Submit: 544  Solved: 261 [Submit][Status][Discuss]

Description

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

Input

第一行三个正整数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),表示第二类限制。

Output

一个正整数,表示集合{Xi}大小的最大值。 如果无解输出NIE。

Sample Input

4 2 2 1 2 3 4 1 4 3 1

Sample Output

3

HINT

|X3=1, X1=X4=2, X2=3 这样答案为3。容易发现没有更大的方案。 我们可以非常显而易见地想到图论算法--差分约束,很快连边就写好了。然后。。。(⊙﹏⊙)不会啊啊啊。 好吧,我们分析一下发现其实本质上来说有一二两种边。第一种边是直接卡死了对于两个X的大小关系。他们之间一定有一个-1关系。而第二种边如果不存在和其他的联通块成环(即被卡死在两个范围之间,而要形成两个范围一定会有第一种边进行的限制。那么也就是说,对于一个联通块,一定有一个范围的限制,但对于一个几个联通块之间的边,他们的大小并不会出现限制。可以发现第一种边是直接将两个点放进了一个强联通分量中,到最后联通块之间的边一定是第二种边,而他们之间的选择不会受到影响,那么答案直接对于所有强联通分量中的答案进行累加就可以了。 差分约束我们设定dis[i][j]表示i最多能比j大多少。我们知道差分约束跑最短路最后是求得最大解。在tarjan求出所有强联通分量之后。由于n很小,直接在各个强联通分量内跑floyd,对于负环直接判断如果dis[i][i]<0就有负环(可以想到对于这样的差分约束不应该有负环)。再在各个强联通分量之间找到最大的dis[i][j]+1就可以了(i大于j,dis[i][j],加上i本身的数字总数),然后全部加起来。 code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
int n,m1,m2;
const int maxm = 200005;
const int maxn = 605;
bool inst[maxn]; int top; int sta[maxn];
int dfx,dfn[maxn],low[maxn],blo[maxn],scc;
int dis1[maxn][maxn];
int la[maxn],nt[maxm],en[maxm],owo;
char buf[1<<20],*p1,*p2;
vector<int>ve[maxn];
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
inline int R()
{
    char t=GC;
    int x=0;
    while(!isdigit(t)) t=GC;
    while(isdigit(t)) x=x*10+t-48,t=GC;
    return x;
}
inline void addedge(int a,int b,int c) {
    en[++owo] = b; nt[owo] = la[a]; la[a] = owo;
    dis1[a][b] = min(dis1[a][b],c);
}
void tarjan(int x) {
    dfn[x]=low[x]=++dfx; sta[++top] = x; inst[x]=1;
    for(int it=la[x];it;it=nt[it]) {
        if(!dfn[en[it]]) {
            tarjan(en[it]);
            low[x] = min(low[x],low[en[it]]);
        } 
        else if(inst[en[it]]) low[x] = min(low[x],dfn[en[it]]);
    }
    if(low[x]==dfn[x]) {
        ++scc; int y;
        do {
            y = sta[top--];
            inst[y] = 0;
            blo[y] = scc;
            ve[scc].push_back(y); 
        }while(y!=x);
    }
}
int main() {
    n=R(); m1 = R(); m2 = R();
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) {
            dis1[i][j] = 0x3f3f3f3f;
        }
        dis1[i][i]  = 0;
    }
    for(int i=1;i<=m1;i++) {
        int x,y; x=R(); y = R();
        addedge(x,y,1); addedge(y,x,-1);
    }
    for(int i=1;i<=m2;i++) {
        int x,y; x=R(); y=R();
        addedge(y,x,0);
    }
    for(int i=1;i<=n;i++) dis1[i][i]  = 0;
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int k=1;k<=n;k++) {
        int idk = blo[k]; 
        for(int i=1;i<=n;i++) {
            int idi = blo[i]; if(idi!=idk) continue;
            for(int j=1;j<=n;j++) {
                int idj = blo[j]; if(idi!=idj) continue;
                dis1[i][j] = min(dis1[i][j],dis1[i][k]+dis1[k][j]);
            }
        }
    }
    for(int i=1;i<=n;i++) if(dis1[i][i]<0) {
        puts("NIE"); return 0;
    }
    int ans = 0;
    for(int k=1;k<=scc;k++) {
        int mama = 0;
        int s = ve[k].size();
        for(int i=0;i<s;i++) {
            for(int j=0;j<s;j++) {
                mama = max(dis1[ve[k][i]][ve[k][j]],mama);
            }
        }
        ans+=mama+1;
    }
    printf("%d",ans);
}
   
posted @ 2018-09-19 13:21  Newuser233  阅读(4)  评论(0)    收藏  举报