题目
http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Index/problemdetail/pid/4532.html
题意
给你n个点 分成两组 让max(碰精队max - 碰精队min, 杠精队max - 杠精队min) 的值 最小
思路
二分图染色缩点 将矛盾的点分为两个线段
找到所有点最大和最小 如果是同一个线段 差即答案
否则将这两个点或线段为为两组 二分答案验证 对于每组矛盾的线段 分别讨论分在两组
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node
{
int l,r;
}b[100005];
struct edge
{
int v,nxt;
}e[200005];
int head[100005];
int vis[100006];
int a[100005];
int top;
int p,f;
int n;
int MAX,MIN;
void add(int u,int v)
{
e[top].v = v;
e[top].nxt = head[u];
head[u] = top++;
}
void dfs(int u,int op)
{
vis[u] = p + op;
b[p+op].l = min(b[p+op].l,a[u]);
b[p+op].r = max(b[p+op].r,a[u]);
for(int i = head[u];i != -1;i = e[i].nxt)
{
int v = e[i].v;
if(vis[v] == vis[u])
{
f = 1;
return ;
}
if(vis[v] == 0)
{
if(op==0) dfs(v,1);
else dfs(v,0);
}
}
}
int ok(int x)
{
for(int i = 1;i <= n;i++)
{
if(vis[i] == 0&&a[i] > MIN + x&&a[i] < MAX - x)
{
return 0;
}
}
for(int i = 1;i <= p;i+=2)
{
if((b[i].r>MIN+x||b[i+1].l<MAX-x)&&(b[i+1].r>MIN+x||b[i].l<MAX-x))
return 0;
}
return 1;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
}
int m;
scanf("%d",&m);
memset(head,-1,sizeof(head));
top = 0;
for(int i = 1;i <= m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
memset(vis,0,sizeof(vis));
p = 1;
f = 0;
for(int i = 1;i <= n;i++)
{
if(head[i] == -1) continue;
else if(vis[i] == 0)
{
b[p].l = b[p+1].l = 100000000 + 10;
b[p].r = b[p+1].r = -1;
dfs(i,0);
p+=2;
if(f)
{
printf("impossible\n");
return 0;
}
}
}
p--;
MAX = -1,MIN = 100000000 + 10;
for(int i = 1;i <= p;i++)
{
MIN = min(MIN,b[i].l);
MAX = max(MAX,b[i].r);
}
for(int i = 1;i <= n;i++)
{
if(vis[i] == 0)
{
MIN = min(MIN,a[i]);
MAX = max(MAX,a[i]);
}
}
for(int i = 1;i <= p;i++)
{
if(b[i].l==MIN&&b[i].r == MAX)
{
printf("%d\n",MAX-MIN);
return 0;
}
}
int l = 0,r = MAX - MIN;
while(l+1<r)
{
int mid = (l + r)>>1;
if(ok(mid))
{
r = mid;
}
else l = mid;
}
if(ok(l)) printf("%d\n",l);
else printf("%d\n",r);
return 0;
}