2018 China Collegiate Programming Contest Final (CCPC-Final 2018) B.Balance of the Force(线段树)

题目

T(T<=20)组样例,每组样例给定n(n<=2e5)个人,

第i个人要么加入光明阵营,获得li(1<=li<=1e9)的值,

要么加入黑暗阵营,获得di(1<=di<=1e9)的值,

m(0<=m<=2e5)个互斥关系,每次给定x,y,表示x和y不能在同一阵营中,

如果不能满足互斥关系,输出IMPOSSIBLE

否则希望n个人中能力值最大值减能力值最小值的差最小,

输出这个最小的差

题解

感觉自己就tm是乱搞的,也不知道这题正解是啥

考虑先按种类并查集合并,类似食物链那道题,

如果互斥的关系已经在相同的关系下,说明无解,

否则转化为k个连通分量,形如每个连通分量要么取121要么取212,

则在[mn[f1],mx[f1]],[mn[f2],mx[f2]]里取一个,

这里暴力讨论了两条线段的相交关系,如果线段有内含关系就只取里面那个,

否则只能是不相交或者相交,肯定一条线段在上一条在下,

考虑[mn[f2],mx[f2]]在上的情形,如果最大值<mx[f2],则最小值只能取mn[f1],

先取mn[f2]作左端点,然后nex的vector用于释放mn[f1],

枚举最大值r,注意r不能小于两个最大值中的最小值,所以是n个min(mn[f1],mn[f2])中的最大值

线段树上二分找到与其对应的最小值l,暴力更新答案即可

代码

#include <bits/stdc++.h>
using namespace std;
const int N=4e5+10;
const int INF=0x3f3f3f3f;
#define fi first
#define se second
#define pb push_back
typedef pair<int,int> P;
P a,b;
vector<int>nex[N];
int t,n,m,c,nowl,l[N],d[N],X[N],Y[N],x[N],p;
int mx[N],mn[N],par[N];
bool vis[N];
int find(int x){
	return par[x]==x?x:par[x]=find(par[x]);
}
bool in(P a,P b){
	return b.fi<=a.fi && a.fi<=b.se && b.fi<=a.se && a.se<=b.se;
} 
void mer(int x,int y){
	x=find(x),y=find(y);
	if(x==y)return;
	par[y]=x;
	mn[x]=min(mn[x],mn[y]);
	mx[x]=max(mx[x],mx[y]);
}
bool same(int x,int y){
	x=find(x),y=find(y);
	return x==y;
}
int main(){
	scanf("%d",&t);
	for(int ca=1;ca<=t;++ca){
		c=0;p=0; 
		scanf("%d%d",&n,&m);
		for(int i=1;i<=2*n;++i){
			par[i]=i;
			nex[i].clear();
			vis[i]=0;
		}
		for(int i=1;i<=m;++i){
			scanf("%d%d",&X[i],&Y[i]);
		}
		for(int i=1;i<=n;++i){
			scanf("%d%d",&l[i],&d[i]);
			x[++p]=l[i];x[++p]=d[i];
		}
		sort(x+1,x+p+1);
		p=unique(x+1,x+p+1)-(x+1);
		for(int i=1;i<=n;++i){
			l[i]=lower_bound(x+1,x+p+1,l[i])-x;
			d[i]=lower_bound(x+1,x+p+1,d[i])-x; 
			mx[i]=mn[i]=l[i];
			mx[i+n]=mn[i+n]=d[i];
		}
		bool ok=1;
		for(int i=1;i<=m;++i){
			if(same(X[i],Y[i])){
				ok=0;
				break;
			}
			mer(X[i],Y[i]+n);
			mer(X[i]+n,Y[i]);
		}
		printf("Case %d: ",ca); 
		if(!ok){
			puts("IMPOSSIBLE");
			continue;
		}
		int lim=0,nowl=INF;
		for(int i=1;i<=n;++i){
			int f1=find(i),f2=find(i+n);
			if(vis[f1]||vis[f2])continue;
			vis[f1]=vis[f2]=1;
			lim=max(lim,min(mx[f1],mx[f2]));
			a=P(mn[f1],mx[f1]);
			b=P(mn[f2],mx[f2]);
			if(in(a,b)){
				nowl=min(nowl,mn[f1]);
			} 
			else if(in(b,a)){
				nowl=min(nowl,mn[f2]);
			}
			else{
				if(mx[f2]>mx[f1])nowl=min(nowl,mn[f2]),nex[mx[f2]].pb(mn[f1]);
				else nowl=min(nowl,mn[f1]),nex[mx[f1]].pb(mn[f2]);
			} 
		}
		int ans=INF;
		for(int i=p;i>=lim;--i){
			nowl=min(nowl,i);
			ans=min(ans,x[i]-x[nowl]);
			for(auto &v:nex[i]){
				nowl=min(nowl,v);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值