[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}大小的最大值。
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
OUTPUT
3
解题报告
显然是一道差分约束
首先,我们来分析一下两种限制条件:
第一种:
$$X_{a}+1=X_{b} \rightarrow X_{a}-X_{b}=-1\rightarrow X_{a}-X_{b}\geqslant -1(and)X_{a}-X_{b}\leqslant -1$$
而:
$$X_{a}-X_{b}\geqslant -1\rightarrow X_{b}-X_{a}\leqslant 1$$
也就是:
$$X_{a}-X_{b}\leqslant -1(and)X_{b}-X_{a}\leqslant 1$$
对于这种限制条件,我们需要从$X_{a}$向$X_{b}$建一条权值为$1$的边,向$X_{b}$向$X_{a}$建一条权值为$-1$的边。
想想为什么?
我们可以这样理解差分约束中两点的边:一条从$A\rightarrow B$,权值为$W$的边,代表$A$要加上$W$才能到$B$
这样就可以解释上面的建边原理了
第二种:
$$X_{c}\leqslant X_{d}\rightarrow X_{c}-X_{d}\leqslant 0$$
对于这种限制条件,我们需要从$X_{d}$向$X_{c}$建一条权值为$0$的边。
建完了图,我们首先要判断是否有解,也就是是否存在负环,我们可以这样做:
先将每个$dis[i][i]$赋值为$0$,然后$FLoyd$跑最短路,然后判断$dis[i][i]$(也就是自己到自己)是否为负
为什么这样做?
我们先想$Floyd$的原理,它是不断枚举两点之间的中间点进行松弛操作,对在点$i$和$j$之间的所有其他点进行一次松弛。那么,我们想,假如没有负环的话,$dis[i][i]$应当是$0$才对,但是,显然负权边的权值小于$0$,也就是说,在松弛的过程中,负权边会松弛$dis[i][i]$,假如形成了负环,那么,必然会有负环上的点的$dis[i][i]$被该负环松弛成了负值,所以这种做法是正确的
然后,我们就该处理答案了
我们先tarjan缩个点,找到一圈强连通分量,我们想,对于每一个强连通分量,把他们连起来的一定是$w=0$的边,假如是$1or-1$的边,那么,一定存在反向的$-1or1$的边将他们反向相连,使他们互相连通,那么,这两个强连通分量就成了一个强连通分量
我们想,我们建的边的权值只有$-1$,$0$,$1$三种,那么,由这三种边连起来的数一定是连续的数
也就是说,我们的强连通分量的最小值$min$到最大值$max$是连续的,且$min$一定是$0$
所以我们对于每一个强连通分量对答案的贡献就是:
$$max-min+1$$
想想为什么?
我们考虑一串连续的自然数,比如说$1$到$9$,我们有$9$个数,这$9$个数是如何得到的呢?
由等差数列求项数公式可以得到:
$$num=\frac{a_{n}-a_{1}}{d}+1$$
其中$num$为项数,$a_{n}$为末项,$a_{1}$为首项,$d$为公差。
然后,又因为$min$为0,所以每一个强连通分量对答案的贡献为:
$$max+1$$
对每一个强连通分量的贡献求和即为答案
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 inline int read(){ 6 int sum(0); 7 char ch(getchar()); 8 for(;ch<‘0‘||ch>‘9‘;ch=getchar()); 9 for(;ch>=‘0‘&&ch<=‘9‘;sum=sum*10+(ch^48),ch=getchar()); 10 return sum; 11 } 12 struct edge{ 13 int e; 14 edge *n; 15 edge():e(0),n(NULL){} 16 }a[200005],*pre[605]; 17 int tot; 18 inline void insert(int s,int e){ 19 a[++tot].e=e; 20 a[tot].n=pre[s]; 21 pre[s]=&a[tot]; 22 } 23 int n,m1,m2; 24 int g[605][605]; 25 inline bool fly(){ 26 for(int i=1;i<=n;++i) 27 g[i][i]=0; 28 for(int k=1;k<=n;++k) 29 for(int i=1;i<=n;++i) 30 for(int j=1;j<=n;++j) 31 if(g[i][k]+g[k][j]<g[i][j]) 32 g[i][j]=g[i][k]+g[k][j]; 33 for(int i=1;i<=n;++i) 34 if(g[i][i]<0) 35 return false; 36 return true; 37 } 38 int dfn[605],low[605],sta[605],zhan[605]; 39 int top,head; 40 bool vis[605]; 41 int cnt; 42 int ans(0); 43 inline void tarjan(int u){ 44 dfn[u]=low[u]=++cnt; 45 sta[++top]=u; 46 vis[u]=1; 47 for(edge *i=pre[u];i;i=i->n){ 48 int e(i->e); 49 if(!dfn[e]){ 50 tarjan(e); 51 low[u]=min(low[u],low[e]); 52 } 53 else 54 if(vis[e]) 55 low[u]=min(low[u],dfn[e]); 56 } 57 if(low[u]==dfn[u]){ 58 head=0; 59 int tmp; 60 while(1){ 61 zhan[++head]=tmp=sta[top--]; 62 vis[tmp]=0; 63 if(tmp==u) 64 break; 65 } 66 tmp=-0x7fffffff; 67 for(int i=1;i<=head;++i) 68 for(int j=1;j<=head;++j) 69 tmp=max(tmp,g[zhan[i]][zhan[j]]); 70 ans+=tmp+1; 71 } 72 } 73 int main(){ 74 memset(pre,NULL,sizeof(pre)); 75 memset(g,0x3f,sizeof(g)); 76 n=read(),m1=read(),m2=read(); 77 for(int i=1;i<=m1;++i){ 78 int x(read()),y(read()); 79 insert(x,y),insert(y,x); 80 g[x][y]=min(g[x][y],1),g[y][x]=min(g[y][x],-1); 81 } 82 for(int i=1;i<=m2;++i){ 83 int x(read()),y(read()); 84 insert(y,x); 85 g[y][x]=min(g[y][x],0); 86 } 87 if(!fly()){ 88 puts("NIE"); 89 return 0; 90 } 91 for(int i=1;i<=n;++i) 92 if(!dfn[i]) 93 tarjan(i); 94 printf("%d",ans); 95 }