【离散化】【扫描线】codeforces102055B Balance of the Force

B. Balance of the Force
time limit per test4.0 s
memory limit per test256 MB
inputstandard input
outputstandard output
A long time ago in a galaxy far, far away, there was a group of knights who mastered the ancient power - the Force. To bring order and balance to the universe, the Force is divided into two categories that conflict with each other: the Jedi, who acts on the light side of the Force through non-attachment and arbitration, and the Sith, who uses the dark side through fear and aggression.

There were N knights who mastered the Force. Each knight could join either the light side or the dark side. When joining the light side, the knight possesses Li Force; when joining the dark side, the knight possesses Di Force.

To maintain order and balance of the universe, the knights wanted to make the Force difference between the most powerful knight and the weakest knight as small as possible. To make things even tougher, some knights did not get along well, and they refused to join on the same side.

Input
The first line of the input gives the number of test cases, T (1≤T≤20). T test cases follow.

For each test case, the first line contains two integers N (1≤N≤2×105) and M (0≤M≤2×105), where N is the number of knights and M is the number of knight pairs that didn’t get along well.

The next M lines each contains two integers x and y (1≤x≠y≤N), describing knight x and knight y didn’t get along well.

The following N lines each contains two integers, Li and Di (1≤Li,Di≤109), representing the Force when the knight joined the light side and the dark side.

Output
For each test case, output one line containing “Case x: y”, where x is the test case number (starting from 1) and y is the minimum difference between the strongest knight and weakest knight, or “IMPOSSIBLE” (quotes for clarify) if it’s impossible for the knights to pick side without violating the given constraints.


 本题出自CCPC总决赛。昨天和队友模拟了这场比赛。被这题坑了很久。当时状态不好也是一个原因,写出来之后感觉其实也不难。
 基本没有什么算法可言,只用到了扫描线,思维的时间不长。所有的数据需要离散化。首先需要判断子图是否为二分图,然后对于每个子图,抽象出一对区间,因为一个二分图,染色有两种相反的方式,第一个区间记录第一种的最小值和最大值,第二个区间记录另一种的最小值和最大值。两个区间对应的方案中必须要选一个。
 找到最大值和最小值的差使之最小,第一想法是二分答案,显然不现实。后来想到了从小到大枚举最小值,然后维护此时的最大值,枚举过程中不断取min就行了。
 想得很6,实际写的时候有点尴尬,总想着怎么处理比较优雅,因为思路还没完全理清楚。我最后的处理方式:先枚举所有区间,把区间对里面终点比较小的值塞入。multiset。然后从小到大枚举最小值,vector记录某个起点所在的区间,最小值枚举到该起点之后遍历这些区间,把它们的终点从multiset里面抹除,如果对应的另一个区间起点在此之后,则把另一个区间终点塞入一个multiset。每一次的答案就是multiset里面的最大值减去当前枚举的最小值。
 还有两个坑点,第一是枚举最小值的上限需要事先求出来,这个上限是每个骑士两种选择中最大值的最小值。第二是如果存在区间对都不在multiset里,也不能再枚举了。

#include<cstdio>
#include<map>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;

struct item
{
	int l,r;
}A[400005];

map<int,int> M;
vector<int> E[200005],in[400005];
multiset<int> S;
int l[200005],r[200005],s[400005],n,T,nn,m,c1,c2,d1,d2,vis[200005],ans,an,R;

bool check(int x, int o)
{
	vis[x]=o;
	if(o==1)
	{
		c1=min(c1,r[x]);
		c2=max(c2,r[x]);
		d1=min(d1,l[x]);
		d2=max(d2,l[x]);
	}
	else
	{
		c1=min(c1,l[x]);
		c2=max(c2,l[x]);
		d1=min(d1,r[x]);
		d2=max(d2,r[x]);
	}
	for(int &j:E[x])
		if(!vis[j])
		{
			if(!check(j,3-o))
				return false;
		}
		else if(vis[j]+vis[x]!=3)
			return false;
	return true;
}

int main()
{
	scanf("%d",&T);
	for(int f=1;f<=T;f++)
	{
		scanf("%d%d",&n,&m);
		M.clear();
		S.clear();
		nn=an=0;
		for(int i=1;i<=n;i++)
			E[i].clear(),vis[i]=0;
		for(int i=1;i<=n*2;i++)
			in[i].clear();
		for(int i=1,x,y;i<=m;i++)
		{
			scanf("%d%d",&x,&y);
			E[x].push_back(y);
			E[y].push_back(x);
		}
		R=1E9;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&l[i],&r[i]);
			s[i*2-1]=l[i],s[i*2]=r[i];
			R=min(R,max(l[i],r[i]));   //R是枚举上限
		}
		sort(s+1,s+2*n+1);
		nn=unique(s+1,s+2*n+1)-s-1;
		for(int i=1;i<=nn;i++)
			M[s[i]]=i;       //离散化一下
		bool flag=true;
		for(int i=1;i<=n&&flag;i++)
			if(!vis[i])
			{
				c1=1E9,c2=0,d1=1E9,d2=0;
				flag&=check(i,1);   //检查二分图
				A[++an]=(item){c1,c2};   //抽象出两个区间
				A[++an]=(item){d1,d2};
			}
		if(!flag)
			printf("Case %d: IMPOSSIBLE\n",f);
		else
		{
			for(int i=1;i<=an;i+=2)
				if(A[i].r<A[i+1].r)
					S.insert(A[i].r),in[M[A[i].l]].push_back(i);
				else
					S.insert(A[i+1].r),in[M[A[i+1].l]].push_back(i+1);
			ans=1E9;   //求出初始状态的S
			for(auto &i:M)
			{
				int val=i.first,pos=i.second;
				if(val>R)
					break;
				ans=min(ans,*S.rbegin()-val);  //更新答案
				for(int &j:in[pos])
				{
					S.erase(S.lower_bound(A[j].r));  //抹除
					if(A[(j-1^1)+1].l>val)
					{
						S.insert(A[(j-1^1)+1].r);
						in[M[A[(j-1^1)+1].l]].push_back((j-1^1)+1);   //添加对应区间
					}
					else
					{
						flag=false;
						break;
					}
				}
				if(!flag)
					break;
			}
			printf("Case %d: %d\n",f,ans);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值