BZOJ2788/POI2012 Festival

Task
有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}大小的最大值。
2<=n<=600, 1<=m1+m2<=100,000.

Solution
1.差分約束:
把Xa+1=Xb轉化爲Xa+1<=Xb,Xa+1>=Xb,建一條a到b邊權爲1的邊,b到a權值爲-1的邊.
把Xc<=Xd轉化爲 Xc-Xd<=0,建一條d到c邊權爲0的有向邊.
建圖之後就可以把問題轉化爲求圖中任意兩點最短路的最大值.

2.強連通縮點:
圖中顯然有環,通過強連通轉化爲DAG,考慮每個強聯通分量之間的關係:
假如有一條從a到b的邊,說明Xa>=Xb,但兩者之間沒有具體大小關係,那麼我們就能保證存在一組解使得a,b的點權值不重複,那麼最終的答案就是每個強連通分量的答案之和.

3.求最短路的最大值:
圖中顯然可能出現負環,那麼就不能用dijkstra算法求最短路.我們可以通過Floyd或者SPFA判斷是否有負環,若存在負環,即無解,否則求最短路.

Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int M=200005;
const int N=605;
int head[N],scc=0,ec=0,n,m1,m2,dfn[N],low[N],Clock=0,stk[N],top=0,id[N],p[N],sum[N][N];
int dis[N][N],inq[N],cnt[N],Q[M*10];
struct node{
    int to,v,nex;
}e[M<<1];
inline void rd(int &res){
    res=0;char c;
    while(c=getchar(),c<48);
    do res=(res<<1)+(res<<3)+(c^48);
    while(c=getchar(),c>=48);
}
void ins(int a,int b,int c){
    e[ec]=(node){b,c,head[a]};
    head[a]=ec++;
}
void Tarjan(int x){
    dfn[x]=low[x]=++Clock;
    stk[++top]=x;
    inq[x]=1;
    for(int i=head[x];~i;i=e[i].nex){
        int y=e[i].to;
        if(!dfn[y]){
            Tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(inq[y])low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        scc++;
        while(1){
            sum[scc][0]++;
            sum[scc][sum[scc][0]]=stk[top];
            inq[stk[top]]=0;
            id[stk[top--]]=scc;
            if(stk[top+1]==x)break;
        }
    }
}
int Find_(int k){
    int l=0,r=0,i,j,mipath=0;
    Q[r++]=p[k];
    int s=p[k];
    dis[s][s]=0;
    while(l<r){
        int x=Q[l++];
        cnt[x]++;
        inq[x]=0;
        if(cnt[x]>sum[k][0])return -1;
        for(i=head[x];~i;i=e[i].nex){
            int y=e[i].to;
            if(id[y]!=k)continue;
            if(dis[s][y]>dis[s][x]+e[i].v){
                dis[s][y]=dis[s][x]+e[i].v;
                if(!inq[y]){
                    Q[r++]=y;
                    inq[y]=1;
                }
            }
        }
    }
    for(i=1;i<=sum[k][0];i++){
        mipath=max(mipath,dis[s][sum[k][i]]);
    }
    return mipath;
}
int solve(){
    int i,ans=0;
    for(i=1;i<=n;i++){
        if(!dfn[i])Tarjan(i);
    }
    for(i=1;i<=scc;i++){
        int b=0;
        for(int j=1;j<=sum[i][0];j++){
            p[i]=sum[i][j];
            for(int k=1;k<=sum[i][0];k++){
                int y=sum[i][k];
                cnt[y]=0,inq[y]=0,dis[p[i]][y]=2e9;
            }
            int a=Find_(i);
            if(a==-1)return -1;
            b=max(b,a); 
        }
        ans+=b+1;
    }
    return ans;
}
int main(){
    memset(head,-1,sizeof(head));
    int i,j,a,b,c,k;
    rd(n);rd(m1);rd(m2);
    for(i=1;i<=m1;i++){
        rd(a);rd(b);//a+1=b,->  a+1<=b,a+1>=b ->  a-b<=-1,b-a<=1   a->b 1
        ins(a,b,1);
        ins(b,a,-1);
    }
    for(i=1;i<=m2;i++){
        rd(a);rd(b);//b>=a  a-b<=0
        ins(b,a,0);
    }
    int res=solve();
    if(~res)printf("%d\n",res);
    else puts("NIE");
    return 0;   
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章