题意
给定 n 个物品,该物品放在 A ,B阵营 有不同的价值,ai,bi。现在需要将 每个物品选择一个阵营,使得整体 n 个物品价值的最大值和最小值之差最小。同时有 m 个限制,每个限制 为 (xi , yi )表示 xi 号物品 和 yi号物品不能放在同一阵营。如果加入该限制后无法有一种合理的安排就是输出 IMPOSSIBLE,否则输出最大值和最小值之差的最小值。
( 1<=n,m<=2e5, 1<=ai,bi<=1e9,1<=xi,yi<=n)
思路
首先用染色把二分图判了,然后跟西安邀请赛一个题目类似,由于一个二分图,对于一个点确定,那么整个连通图的阵营情况就明了了,单个点也可以看作一个二分图,因此,对于每个联通图去求出他的两种二元组(mi,ma),(西安那个题然后转化为了背包问题,这里则是数据结构)。然后问题抽象为,给定 block 个元素,每个元素可以有两个二元组,必须选择其中一个,求一种选择方式使得最大值最小值之差最小。
然后我考虑贪心,动归无果… 感觉没办法只能枚举最小值或者最大值,然后就陷入了不知咋求未被枚举的量的最优值。模拟赛的时候我就陷入了二分的僵局,虽然觉得二分不太对,但也没发现错在哪,求懂的巨巨可以评论解释一下。
补题才知道… 这个模型主要麻烦的点在于每个元素有两个二元组,会有一个选择问题。当不知道怎么做的时候排个序总是好的,起码可以解决一维的偏序。然后将所有元素的两个二元组全都按右端点从小到大排序,每次遇到遇到一个二元组,将其 mi 插入线段树,如果其同一个元素的对应二元组已经存在了,由于我们枚举最大值,对于同一个元素的二元组我们肯定选择mi较大的那个,因此去更新对应位置的最小值,取大,但是整个线段树是维护的整体最小值。那何时可以更新答案呢,当已经出现了所有的联通块的时候就可以更新答案。因为之前没有把图全覆盖的时候更新答案不合法,也没意义。所以要用一个 blkcnt 记录已经出现的联通块的个数。
然后看到这里可能有一个疑问,会不会存在 一个元素同时选择了两个二元组来更新了答案呢? 实际上是不可能的。设 排在前面的一个二元组为 (x1,y1) 后一个二元组为 (x2,y2) ,因为按最大值排序可知, y1 <= y2,这里设 y1 < y2,如果出现那种选了两个更新答案的情况,即存在 x1 > x2,且 x1 是目前的全局最小值。然后分类讨论:
- 如果 (x1,y1) 的时候连通块已经全覆盖,那么如果在后面出现(x2,y2),那因为 y2 > y1 所以肯定不如 y1-x1的答案优。(即使这两个二元组相邻出现)
- 如果(x1,y1)的时候连通块还没全覆盖,也就是还未统计答案。然后(x2,y2)出现的时候连通块已经全覆盖,那么因为这两个来自同一个联通块,所以他们不可能连续出现,不然那连通块个数没有增加,(x2,y2)出现的时候仍然不必统计答案,设中间夹着一个(x3,y3),那么可知 y3<=y3,所 y3-x1 <= y2-x1 ,所以不会出现。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 200005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define ls x<<1
#define rs x<<1|1
#define mod 1000000007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
ll x=0,f=1ll;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
int T,n,m;
struct node{int v,nxt;}ed[maxn<<1];
int head[maxn],tot;
void add(int x,int y){ed[++tot]={y,head[x]};head[x]=tot; }
int a[maxn][2];
int col[maxn];
struct nbnb{int mi,ma,id;
bool operator < (const nbnb & rid)const{
return ma < rid.ma;
}
}seg[maxn<<1];
int cnt;
bool dfs(int x,int y,int c){/// c是颜色 0,1 初始要把col搞成-1
col[x]=c;
bool flag=0;
for(int i=head[x];i;i=ed[i].nxt){
int v=ed[i].v;
if(v==y)continue;
if(col[v]>=0){
if(col[v]^col[x]==0)flag=1;
}
else if(dfs(v,x,c^1))flag=1;
}
return flag;
}
bool vis[maxn][2];
void giao(int x,int c,int typ,int num){
vis[x][typ]=1;
seg[num].mi=min(seg[num].mi,a[x][c]);
seg[num].ma=max(seg[num].ma,a[x][c]);
for(int i=head[x];i;i=ed[i].nxt){
int v=ed[i].v;
if(vis[v][typ])continue;
giao(v,c^1,typ,num);
}
}
int t[maxn<<2];
void pushup(int x){t[x]=min(t[ls],t[rs]);}
void build(int x,int l,int r){
t[x]=INF;
if(l==r){return ;}
int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
}
void update(int x,int l,int r,int pos,int val){
if(l==r){
if(t[x]==INF)t[x]=val;
else t[x]=max(t[x],val);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)update(ls,l,mid,pos,val);
else update(rs,mid+1,r,pos,val);
pushup(x);
}
bool blkvis[maxn];
int blkcnt;
int main()
{
T=read();
int kase=0;
while(T--){
n=read();m=read();
inc(i,0,n)vis[i][0]=vis[i][1]=head[i]=0;
tot=0;
int x,y;
inc(i,1,m){
x=read();y=read();
add(x,y);add(y,x);
}
inc(i,1,n){
a[i][0]=read();a[i][1]=read();
}
printf("Case %d: ",++kase);
bool flag=0;
int block=cnt=0;
inc(i,0,n)col[i]=-1;
inc(i,1,n)if(col[i]==-1){flag|=dfs(i,0,1);block++;}
inc(i,1,block*2){seg[i].ma=0;seg[i].mi=INF; }
inc(i,0,block)blkvis[i]=0;
blkcnt=0;
if(flag)printf("IMPOSSIBLE\n");
else {
inc(i,1,n){
if(vis[i][0]==0){
giao(i,0,0,++cnt);
giao(i,1,1,++cnt);
seg[cnt-1].id=cnt/2;
seg[cnt].id=cnt/2;
}
}
sort(seg+1,seg+cnt+1);
build(1,1,block);
int ans=INF;
inc(i,1,cnt){
if(blkvis[seg[i].id]==0){
blkcnt++;
blkvis[seg[i].id]=1;
}
update(1,1,block,seg[i].id,seg[i].mi);
if(blkcnt==block)ans=min(ans,seg[i].ma-t[1]);
}
printf("%d\n",ans);
}
}
return 0;
}