[二分+2-SAT]POJ 2749 Building roads 题解
题目大意
给出 n n n个谷仓和2个中转站的坐标,要求每个谷仓都必须连且只连接其中一个中转站,有 k 1 k1 k1对谷仓由于某种原因不能连在同一个中转站,还有 k 2 k2 k2对谷仓由于另某种原因一定要连载同一个中转站,平面上两点路径等于两点的曼哈顿距离,问所有谷仓连接后所有两个谷仓的最长路的最小值是多少。
解题分析
“连且只连接其中一个中转站”,这明显是一个2-SAT模型,但2-SAT本身只能求 O ( n 2 ) O(n^2) O(n2)字典序最小解和 O ( n ) O(n) O(n)任意解,无法做到求出在某种方案下的最优解,所以……最大值最小,二分,再用2-SAT,构思一下发现能行,那么就可以二分枚举答案,2-SAT判断答案。那么问题还有一个,就是2-SAT建图的问题,厌恶和喜欢的这两种我们已经讨论过,但是如果枚举了答案ans,那么还有一些关系因为距离过长也要ban掉。一开始预估数组大小和建边要仔细,否则会被卡……
解题报告
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=505,maxm=1005,maxe=(maxm+maxn*maxn)<<3;
int n,ans,ka,kb,ds,sx[2],sy[2],aa[maxm],ab[maxm],ba[maxm],bb[maxm],ax[maxn],ay[maxn];
int tot,son[maxe],nxt[maxe],lnk[maxm],ti,k,top,low[maxm],dfn[maxm],stk[maxm],SCC[maxm];
bool instk[maxm];
inline void readi(int &x){
x=0; char ch=getchar(),lst='+';
while ('0'>ch||ch>'9') {lst=ch; ch=getchar();}
while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
if (lst=='-') x=-x;
}
int absi(int x){return x>0?x:-x;}
int getd(int x,int y){return absi(ax[x]-sx[y])+absi(ay[x]-sy[y]);}
void _init(){
freopen("roads.in","r",stdin);
freopen("roads.out","w",stdout);
readi(n); readi(ka); readi(kb);
readi(sx[0]); readi(sy[0]); readi(sx[1]); readi(sy[1]);
ds=absi(sx[0]-sx[1])+absi(sy[0]-sy[1]);
for (int i=0;i<n;i++){readi(ax[i]); readi(ay[i]);}
for (int i=1;i<=ka;i++){readi(aa[i]); readi(ab[i]); aa[i]--; ab[i]--;}
for (int i=1;i<=kb;i++){readi(ba[i]); readi(bb[i]); ba[i]--; bb[i]--;}
}
void _add(int x,int y){son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;}
void _build(int x){
tot=0; memset(lnk,0,sizeof(lnk));
for (int i=1;i<=ka;i++){ //厌恶关系
_add(aa[i]<<1,ab[i]<<1^1); _add(ab[i]<<1,aa[i]<<1^1);
_add(aa[i]<<1^1,ab[i]<<1); _add(ab[i]<<1^1,aa[i]<<1);
}
for (int i=1;i<=kb;i++){ //喜欢关系
_add(ba[i]<<1,bb[i]<<1); _add(ba[i]<<1^1,bb[i]<<1^1);
_add(bb[i]<<1,ba[i]<<1); _add(bb[i]<<1^1,ba[i]<<1^1);
}
for (int i=0;i<n-1;i++)
for (int j=i+1;j<n;j++){
if (x<getd(i,0)+getd(j,0)) {_add(i<<1,j<<1^1); _add(j<<1,i<<1^1);}
//i和j不能都到0
if (x<getd(i,1)+getd(j,1)) {_add(i<<1^1,j<<1); _add(j<<1^1,i<<1);}
//i和j不能都到1
if (x<getd(i,0)+getd(j,1)+ds) {_add(i<<1,j<<1); _add(j<<1^1,i<<1^1);}
//i到0则j不能到1或j到1则i不能到0
if (x<getd(i,1)+getd(j,0)+ds) {_add(i<<1^1,j<<1^1); _add(j<<1,i<<1);}
//i到1则j不能到0或j到0则j不能到1
}
}
void _dfs(int x){
dfn[x]=low[x]=++ti; stk[++top]=x; instk[x]=1;
for (int j=lnk[x];j;j=nxt[j])
if (!dfn[son[j]]) {_dfs(son[j]); low[x]=min(low[x],low[son[j]]);}
else if (instk[son[j]]) low[x]=min(low[x],dfn[son[j]]);
if (dfn[x]==low[x]){
int y; k++;
do{y=stk[top--]; instk[y]=0; SCC[y]=k;
}while(x!=y);
}
}
bool _check(int x){
_build(x); ti=top=k=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(instk,0,sizeof(instk));
for (int i=0;i<n<<1;i++) if (!dfn[i]) _dfs(i);
for (int i=0;i<n<<1;i+=2) if (SCC[i]==SCC[i^1]) return 0;
return 1;
}
void _solve(){
int L=0,R=12000000; ans=-1;
while (L<=R){
int mid=(R-L)/2+L;
if (_check(mid)) {ans=mid; R=mid-1;}
else L=mid+1;
}
printf("%d",ans);
}
int main()
{
_init();
_solve();
return 0;
}