P3530[POI2012]FES-Festival
题目
有一个数列
a
a
a 。现给定多组限制,限制分成 2 类,第一类是
a
x
+
1
=
a
y
ax+1=ay
ax+1=ay ,有
m
1
m1
m1 个; 第二类是
a
x
≤
a
y
ax≤ay
ax≤ay ,有
m
2
m2
m2 个。求这些数最多有多少种不同的取值。
详情请查看题目
分析
首先建出差分约束系统。然后它要你求的是不同成绩的最大数量,而差分约束只擅长做(两个变量的差的最大 / 小值)这种问题,如何转化过去呢?
注意到对于任意差分约束系统,这个点与点的连通性是非常混乱的,无法判断点和点和点和点之间到底能不能拉开,还是必须重叠的什么情况。我们考虑一个强连通图,这里面每个点到每个点都可以互相到达,也就是说任意两个变量的差都有有限的上下限。那就很好了呀,我们至少知道了它的不同成绩的数量的一个上限:我们总要选两个点当作值域的两端对不对,而每两个点的差都是有有限上限的,于是取那个最大的距离(也就是最大的一对点之间的最短路)就是上限了。接下来考虑上限能不能达到,很容易证明可以,因为在那条最大的最短路上,一路走的过程中每次加一,都一定是第一种限制做的功劳,而第一种限制是定量限制,限制死了的,所以一旦把值域的两端确定下来,中间都填的满满的。
至此我们已经知道一个强连通图的求法了。考虑将原图强连通分解,然后每个 SCC 求出来答案,再缩点发现是个 DAG。DAG 也是个连通性特殊的有向图,它也好办了呀,没有任意两个 SCC 间有相互的限制,也就是说我们可以沿着边的方向将两个 SCC 之间的距离无限拉大,这样每个 SCC 就是独立的了,把所有 SCC 答案加起来也就达到了上限。
代码
#include<bits/stdc++.h>
using namespace std;
int n,m1,m2,d[610][610];
struct edge
{
int x,w;
};
vector<edge> v[610];
int sum,st[610],top,ans,res;
int dfn[610],low[610],ins[610],col[610],cnt[610];
void Tarjan(int x)
{
low[x]=dfn[x]=++res;
st[++top]=x;
ins[x]=1;
int y;
for(int i=0;i<v[x].size();i++)
{
y=v[x][i].x;
if(!dfn[y])
{
Tarjan(y);
low[x]=min(low[y],low[x]);
}
else if(ins[y])low[x]=min(dfn[y],low[x]);
}
if(dfn[x]==low[x])
{
sum++;
do
{
y=st[top--];
col[y]=sum;
ins[y]=0;
}while(y!=x);
}
}
int a[610];
int main()
{
scanf("%d%d%d",&n,&m1,&m2);
memset(d,0x3f3f,sizeof(d));
for(int i=1;i<=n;i++)d[i][i]=0;
for(int i=1,x,y;i<=m1;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(edge{y,1});
v[y].push_back(edge{x,-1});
d[x][y]=min(d[x][y],1);
d[y][x]=min(d[y][x],-1);
}
for(int i=1,x,y;i<=m2;i++)
{
scanf("%d%d",&x,&y);
v[y].push_back(edge{x,0});
d[y][x]=min(d[y][x],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(col[i]!=col[k]||d[i][k]==d[0][0])continue;
for(int j=1;j<=n;j++)
{
if(col[i]!=col[j]||d[k][j]==d[0][0])continue;
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
for(int i=1;i<=n;i++)
{
if(d[i][i])
{
printf("NIE");
return 0;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(col[i]==col[j])a[col[i]]=max(a[col[i]],d[i][j]);
}
}
for(int i=1;i<=sum;i++)ans+=a[i]+1;
printf("%d",ans);
return 0;
}