[Poi2012]Festival 变态 差分约束题

题意

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

(自己注:求集合中不相同的的元素最多)

solution

(最后发现我tarjan写挂了......)

1.建图

Xa+1==Xb   ->   Xa+1<=Xb && Xa+1>=Xb

Xc<=Xd   ->   Xc-Xd<=0

2.floyd判负环(无解)&&求出两点之间的最短距离

初始化mp[i][j]为INF

记录下边后,把mp[i][j]=0

floyd一遍,如果mp[i][i]<0,说明存在负环   (因为自己到自己不是0,肯定是走了一个负环回来,变小了,一定有负环嘛)

3.tarjan缩点

最后的ans是所有强联通分量 max-min+1 的和

证明:

对于每一个强连通分量,把他们连起来的一定是  w=0  的边

如果不是,一定是{1,-1},但是两个点之间的边是相反关系,如果有1/-1边连强连通分量,那么一定有一条-1/1的边

就不是两个强连通分量了

而又因为边只有{0,1,-1}的边,所以从最小值~最大值一定是连续的,min又一定是0,因为有1/-1的边

所以ans=∑(max-min+1)

 

对于每一个强连通分量,求出最短路的最大值+1,即为此强连通分量对ans的贡献

累加起来即可

 

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #define mem(a,b) memset(a,b,sizeof(a))
  5 using namespace std;
  6 const int N=606;
  7 struct son
  8 {
  9     int v,next;
 10 };
 11 son a1[200066];
 12 int first[200066],e;
 13 void addbian(int u,int v)
 14 {
 15     a1[e].v=v;
 16     a1[e].next=first[u];
 17     first[u]=e++;
 18 }
 19 
 20 int n,m1,m2;
 21 int u,o;
 22 int mp[N][N];
 23 int dfn[N],low[N],now,ans;
 24 int zhan[200006],he,flag[N];
 25 int ji[N],jinow;
 26 
 27 void jilu()
 28 {
 29     int temp=-0x7fffffff;
 30     for(int i=1;i<=jinow;++i)
 31       for(int j=1;j<=jinow;++j)
 32       {
 33             if(mp[ji[i]][ji[j]]>temp)
 34               temp=mp[ji[i]][ji[j]];
 35         }
 36     ans+=temp+1;
 37 }
 38 
 39 void tarjan(int x)
 40 {
 41     //printf("x=%d
",x);
 42     low[x]=dfn[x]=++now;
 43     flag[x]=1;zhan[++he]=x;
 44     for(int i=first[x];i!=-1;i=a1[i].next)
 45     {
 46         int temp=a1[i].v;
 47         //cout<<temp;
 48         if(dfn[temp]==-1)
 49         {
 50             tarjan(temp);
 51             if(low[x]>low[temp])
 52               low[x]=low[temp];
 53         }
 54         else
 55           if(flag[temp])
 56             if(low[x]>dfn[temp])
 57               low[x]=dfn[temp];
 58     }
 59     
 60     if(low[x]==dfn[x])
 61     {
 62         int temp;
 63         jinow=0;
 64         while(1)
 65         {
 66             temp=zhan[he--];flag[temp]=0;
 67             ji[++jinow]=temp;
 68             if(temp==x)
 69               break;
 70         }
 71         jilu();
 72     }
 73 }
 74 
 75 int floyd()
 76 {
 77     int temp;
 78     for(int i=1;i<=n;++i)
 79       mp[i][i]=0;
 80     for(int k=1;k<=n;++k)
 81       for(int i=1;i<=n;++i)
 82         for(int j=1;j<=n;++j)
 83         {
 84                 temp=mp[i][k]+mp[k][j];
 85           if(mp[i][j]>temp)
 86             mp[i][j]=temp;
 87             }
 88     for(int i=1;i<=n;++i)
 89       if(mp[i][i]<0)
 90         return 0;
 91     return 1;
 92 }
 93 
 94 int main(){
 95     
 96     //freopen("1.txt","r",stdin);
 97     
 98     mem(first,-1);
 99     mem(mp,0x3f);
100     mem(dfn,-1);
101     
102     scanf("%d%d%d",&n,&m1,&m2);
103     for(int i=1;i<=m1;++i)
104     {
105         scanf("%d%d",&u,&o);
106         addbian(u,o);
107         addbian(o,u);
108         mp[u][o]=min(1,mp[u][o]);
109         mp[o][u]=min(-1,mp[o][u]);//有可能会重复 
110     }
111     for(int i=1;i<=m2;++i)
112     {
113         scanf("%d%d",&u,&o);
114         addbian(o,u);
115         mp[o][u]=min(0,mp[o][u]);
116     }
117     if(floyd()==0)
118     {
119         printf("NIE
");
120         return 0;
121     }
122 //    cout<<0;
123     for(int i=1;i<=n;++i)
124       if(dfn[i]==-1)
125         tarjan(i);
126     
127     cout<<ans<<endl;
128     //while(1);
129     return 0;
130 }
code

 

 

原文地址:https://www.cnblogs.com/A-LEAF/p/7351610.html