2018 ccpc final:B. Balance of the Force (枚举)

14 篇文章 0 订阅
5 篇文章 0 订阅

题目大意:有n个骑士,每个骑士可以加入光阵营或暗阵营,并且获得一个力量,分别为L和D,现在有m对骑士不能在同一个阵营,问力量差值(最大值和最小值的差值)最小是多少?如果不存在阵营满足仇恨骑士不在同一个阵营内,则输出IMPOSSIBLE。

解法:首先二分图判定一下是否存在奇环,若存在奇环则输出IMPOSSIBLE。
按仇恨关系建边形成的图,注意到一个连通分量只要一个人选定加入哪个阵营,其他人的阵营也就确定了。因此一个连通分量只有两种选择方案,将图染色按连通分量缩点后,求出每一个连通分量的两种方案的力量的最大值和最小值,中间的值我们是不关心的,它们对答案没有贡献。

设连通分量有k个,则这时一共有2 * k个方案,将这2 * k个方案排序,按顺序枚举力量的最大值,当第i个方案前已经凑够k个连通分量时,可以查找最小值,更新答案。

分析:首先答案的贡献肯定来自于某一个方案的最大值扣去某一个方案的最小值,不难想到可以枚举最大值是哪个方案的最大值,然后查找所有其他方案的最小值(其他方案的最大值要比这个最大值小才行),来得到答案。无序的情况很难枚举,排序之后可以按顺序枚举。但要查找前面那些方案中的最小值又很费时间,可以用线段树维护,单次查找复杂度降到logn,注意到一个连通分量有两个选择方案,当你枚举到的方案,所在的连通分量的另一个方案在你查找的区间范围内时,必须要抹去这个方案对答案的贡献,否则会出错。

(借鉴一波dfs的写法)

#include<bits/stdc++.h>
using namespace std;
#define mes(a,b) memset(a,b,sizeof(a))
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;
int t,n,m;
int color[maxn << 1];
int power[maxn << 1][2];
vector<int> g[maxn << 1];
struct node {
	int id,mx,mn;
	bool operator < (const node &a) const {
		return mx < a.mx;
	}
}a[maxn << 2];
int sz = 0,cnt = 0,sum[maxn << 2],vis[maxn << 2],pos[maxn << 2];
int tree[maxn << 3];
bool flag;
void init() {
	sz = cnt = 0;
	mes(tree,inf);
	for(int i = 0; i <= n; i++) {
		g[i].clear();
		color[i] = -1;
		vis[i] = pos[i] = sum[i] = 0;
	}
	flag = true;
}
bool dfs(int s,int fa,node &x,node &y) {
	x.mn = min(x.mn,power[s][0]);
	x.mx = max(x.mx,power[s][0]);
	y.mn = min(y.mn,power[s][1]);
	y.mx = max(y.mx,power[s][1]);
	for(int i = 0; i < g[s].size(); i++) {
		int v = g[s][i];
		if(v == fa) continue;
		if(color[v] == -1) {
			color[v] = color[s] ^ 1;
			if(!dfs(v,s,y,x)) return false;
		}
		else if(color[v] == color[s]) return false;
	}
	return true;
}
void update(int rt,int l,int r,int p,int v) {
	if(l == r) {
		tree[rt] = v;
		return ;
	}
	int mid = l + r >> 1;
	if(p <= mid) update(rt << 1,l,mid,p,v);
	else update(rt << 1 | 1,mid + 1,r,p,v);
	tree[rt] = min(tree[rt << 1],tree[rt << 1 | 1]); 
}
int main() {
	scanf("%d",&t);
	int ca = 0;
	while(t--) {
		scanf("%d%d",&n,&m);
		init();
		for(int i = 1; i <= m; i++) {
			int x,y;
			scanf("%d%d",&x,&y);
			g[x].push_back(y);
			g[y].push_back(x);
		}
		for(int i = 1; i <= n; i++) {
			scanf("%d%d",&power[i][0],&power[i][1]);
		}
		bool f = true;
		for(int i = 1; i <= n; i++) {
			if(color[i] == -1) {
				cnt++;
				color[i] = 0;
				sz++;
				a[sz].id = i;a[sz].mn = inf;a[sz].mx = 0;
				sz++;
				a[sz] = a[sz - 1]; 
				if(!dfs(i,-1,a[sz - 1],a[sz])) {
					f = false;
					break;
				}				
			} 
			if(!f) break;
		}
		printf("Case %d: ",++ca);
		if(!f) {
			puts("IMPOSSIBLE");
			continue;
		}
		sort(a + 1,a + sz + 1);
		for(int i = 1; i <= sz; i++) {
			sum[i] = sum[i - 1];
			if(!vis[a[i].id]) {
				sum[i]++;
				vis[a[i].id] = 1;
			}
		}
		int cmx,cmn;
		int res = inf;
		for(int i = 1; i <= sz; i++) {
			cmx = a[i].mx,cmn = a[i].mn;
			if(sum[i] == cnt) {
				if(pos[a[i].id])
					update(1,1,sz,pos[a[i].id],inf);
				cmn = min(cmn,tree[1]);
				res = min(res,cmx - cmn);
			}
			if(pos[a[i].id]) {
				int t = max(a[i].mn,a[pos[a[i].id]].mn);
				update(1,1,sz,pos[a[i].id],t);
				update(1,1,sz,i,t);
			}
			else {
				pos[a[i].id] = i;
				update(1,1,sz,i,a[i].mn);
			}
		}
		printf("%d\n",res);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值