【bzoj2788】Festival
Description
有\(n\)个正整数\(X_1,X_2,...,X_n\),再给出\(m1+m2\)个限制条件,限制分为两类:
1、给出\(a,b (1<=a,b<=n)\),要求满足\(X_a + 1 = X_b\)
2、 给出\(c,d (1<=c,d<=n)\),要求满足\(X_c <= X_d\)
在满足所有限制的条件下,求集合\(\{X_i\}\)大小的最大值,如果无解输出"NIE"
数据范围$2<=n<=600, 1<=m1+m2<=100,000 $
Solution
同样也是。。借这题补个档:差分约束系统
差分约束系统的话是将形如\(x_i-x_j<=a\)这样的式子转化成有向图中的一条\(j\rightarrow i\)长度为\(a\)的边,然后对于一个不等式组,我们可以用这样的方式建一个有向图出来,不等式组中未知数\(x_j-x_i\)的最大值即为\(x_i\)到\(x_j\)的最短路径长度,如果说有向图中存在负环那么就是无解的情况,如果说不存在\(x_i\)到\(x_j\)的路径则有无数组解(因为这就说明这两个未知数之间没有约束关系)
具体一点的话这里举个小栗子例子方便理解
考虑这样一个“三角”不等式组:
然后我们可以建出这样的一个有向图:
然后我们考虑一下\(C-A\)的最大值,从式子来看应该是\(min(b,a+c)\),所以就是\(A\)到\(C\)的最短路,式子更多的情况也是一样的
这里贴一个整理比较全的博(各种题目qwq):Portal
然后我们回到这道题
这题的那个式子。。长得就是差分约束系统的样子qwq,只不过我们需要稍微处理一下:
然后建出有向图,然后我们通过负环就可以直接判掉无解的情况了
接下来只考虑有解的情况
为了方便思考我们先假装已经缩好了点,每个点代表一个强联通分量
这时这个新图里面一定没有环了,并且由于我们的建图方式,所以我们可以肯定缩完点之后的图中所有的边权都是\(0\),否则一定会有一条反向的边,那就与缩完点的性质矛盾了(这个性质十分关键)
我们可以发现,由于题目要求的是集合大小,也就是不同的元素个数,如果两个点不在同一个强联通分量里面,那么这两个点的取值一定能构造出一种答案使得两个点的取值不同,再推广一下,我们会发现其实两个强联通分量对答案的贡献是不会相互影响的,也就是说我们只需要将每个强联通分量的答案算出来然后最后求和就可以得到最后的答案了
现在的问题是怎么求一个强联通分量内部的答案
我们挑一个强联通分量进行考虑,如果说我们挑的这个强联通分量中差值最大的两个数\(x_1,x_2\),假设他们的最大差值(也就是最短路)为\(a\)
考虑一下这个有向图的特殊性质:边权\(\in \{-1,0,1\}\),并且因为这个图除去强联通分量外不存在环了,所以强联通分量中的两个点之间的最短路一定走的是强联通分量中的边,所以如果说最大差值为\(a\)(也就是最短路长度为\(a\)),这条路径一定是由若干条\(0\),若干条\(1\),若干条\(-1\)的边构成的,而那就说明增大只能\(+1\)这样来增大,所以中间包含的所有的数一定都能被取到,也就是说有\(a+1\)个不同的数
所以一个强联通分量对答案的贡献就是\(a+1\)了,然后这题就十分愉快滴做完啦
一个小trick:因为这题的点数很少,所以我们可以直接用Floyd来求最短路(反正你查询的时候。。每个点出发的最短路都要查。。),判负环的话直接跑完Floyd之后看一下是否存在一个\(i\)满足\(dis[i][i]<0\)就好了
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=610,M=100010,inf=2147483647;
int dis[N][N],mp[N][N];
int h[N],dfn[N],low[N],st[N],inst[N],pre[N],mx[N];
int id[N];
int n,m1,m2,dfn_t,top,ans,cnt;
void tarjan(int x){
dfn[x]=low[x]=++dfn_t; inst[x]=1; st[++top]=x;
for (int i=1;i<=n;++i){
if (i==x||!mp[x][i]) continue;
if (!dfn[i]){
tarjan(i);
low[x]=min(low[i],low[x]);
}
else if (inst[i]){
low[x]=min(low[x],dfn[i]);
}
}
int u;
if (low[x]==dfn[x]){
++cnt; u=st[top];
while (u!=x){
id[u]=cnt;
inst[u]=false;
u=st[--top];
}
id[x]=cnt;
inst[u]=false;
--top;
}
}
int floyd(){
for (int k=1;k<=n;++k)
for (int i=1;i<=n;++i){
if (dis[i][k]==inf) continue;
for (int j=1;j<=n;++j){
if (dis[k][j]==inf) continue;
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
void add(int x,int y,int d){
dis[x][y]=min(dis[x][y],d);
mp[x][y]=true;
}
void solve(){
dfn_t=0; top=0;
for (int i=1;i<=n;++i)
if (!dfn[i]) tarjan(i);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j){
if (i==j||id[i]!=id[j]) continue;
mx[id[i]]=max(mx[id[i]],dis[i][j]);
}
ans=0;
for (int i=1;i<=cnt;++i)
ans+=mx[i]+1;
printf("%d\n",ans);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y;
scanf("%d%d%d",&n,&m1,&m2);
memset(mp,false,sizeof(mp));
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
dis[i][j]=inf;
for (int i=1;i<=m1;++i){
scanf("%d%d",&x,&y);
add(x,y,1);//1<=y-x<=1
add(y,x,-1);
}
for (int i=1;i<=m2;++i){
scanf("%d%d",&x,&y);
add(y,x,0);//x-y<=0
}
floyd();
bool flag=true;
for (int i=1;i<=n&&flag;++i)
if (dis[i][i]<0) flag=false;
if (!flag){printf("NIE\n");return 0;}
solve();
}