The 2018 ACM-ICPC Asia Qingdao Regional Contest I.Soldier Game

The 2018 ACM-ICPC Asia Qingdao Regional Contest I.Soldier Game

题目链接

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4066

题目大意

你需要把 $n$ 个士兵分成若干组,满足每组都有 $1$ 个或 $2$ 个士兵,且要保证士兵编号连续。求权值最大的组的权值减去权值最小的组的权值为多少。

题目分析

题目的意思为,用长度为 $1$ 和 $2$ 的线段不重叠覆盖序列,使得线段中最大值减去最小值最小。如果最小值能够固定,那么题目的做法可以类比最小生成树的做法,将所有线段按权值大小由小到大依次添加进去,维护是否可以完整覆盖。对于本体来说,可以先将所有 $n$ 个长度为 $1$ 的线段和 $n – 1$ 长度为 $2$ 的线段先提取出来,按照权值有效到大排序,通过枚举最小值,维护最小的最大值即可。在这个过程中,需要维护是否能完整覆盖。可以将为每个线段 $(l, r)$ 变成 $l$ 指向 $r + 1$ 的一条有向边,维护 $1$ 与 $n + 1$ 的连通性即可。由于枚举最小值时会有修改和删除操作,需要数据结构来维护。可以使用线段树维护区间的连通性,合并时考虑中间时直接连接还是跳一个连接。枚举所有可行的最小值,维护对应的最大值,对所有结果取最小值即为答案

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <map>

using namespace std;

const int maxn = 2e5 + 5;

int n, m, t;

pair <long long, pair <int, int> > f[maxn]; 

long long a[maxn];

int b[maxn][2];

struct node {
	int l, r;
	int lc, rc;
	bool v[2][2];
};

struct st {
	node tree[maxn];
	int root;
	int tc;
	
	int merge(node &ret, node x, node y){
		if((x.v[0][0] and y.v[0][0] and b[x.r][0]) or (x.v[0][1] and y.v[0][0] and b[x.r - 1][1]) or (x.v[0][0] and y.v[1][0] and b[x.r][1])){
			ret.v[0][0] = true;
		}else{
			ret.v[0][0] = false;
		}
		if((x.v[1][0] and y.v[0][0] and b[x.r][0]) or (x.v[1][1] and y.v[0][0] and b[x.r - 1][1]) or (x.v[1][0] and y.v[1][0] and b[x.r][1])){
			ret.v[1][0] = true;
		}else{
			ret.v[1][0] = false;
		}
		if((x.v[0][0] and y.v[0][1] and b[x.r][0]) or (x.v[0][1] and y.v[0][1] and b[x.r - 1][1]) or (x.v[0][0] and y.v[1][1] and b[x.r][1])){
			ret.v[0][1] = true;
		}else{
			ret.v[0][1] = false;
		}
		if((x.v[1][0] and y.v[0][1] and b[x.r][0]) or (x.v[1][1] and y.v[0][1] and b[x.r - 1][1]) or (x.v[1][0] and y.v[1][1] and b[x.r][1])){
			ret.v[1][1] = true;
		}else{
			ret.v[1][1] = false;
		}
		if(x.l == x.r){
			ret.v[1][0] = y.v[0][0];
			if(y.r - y.l == 1){
				ret.v[1][1] = true;
			}
		}
		if(y.l == y.r){
			ret.v[0][1] = x.v[0][0];
			if(x.r - x.l == 1){
				ret.v[1][1] = true;
			}
		}
		return 0;
	}
	
	int update_tree(int pos, int l, int r){
		int mid = (tree[pos].l + tree[pos].r) / 2;
		
		if(tree[pos].l != tree[pos].r){
			if(r <= mid){
				update_tree(tree[pos].lc, l, r);
			}else if(l <= mid and mid < r){
				update_tree(tree[pos].lc, l, mid);
				update_tree(tree[pos].rc, mid + 1, r);
			}else{
				update_tree(tree[pos].rc, l, r);
			}
			
			merge(tree[pos], tree[tree[pos].lc], tree[tree[pos].rc]);
		}
		
		return 0;
	}
	
	int init_tree(int l, int r){
		int mid = (l + r) / 2;
		int pos = ++tc;
		
		tree[pos].l = l;
		tree[pos].r = r;
		memset(tree[pos].v, 0, sizeof(tree[pos].v));
		
		if(l == r){
			tree[pos].v[0][0] = true;
		}else{
			tree[pos].lc = init_tree(l, mid);
			tree[pos].rc = init_tree(mid + 1, r);
		}
		
		return pos;
	}
	
	bool query(){
		return tree[root].v[0][0];
	}
	
	int update(int x, int y){
		update_tree(root, x, y);
		return 0;
	}
	
	int init(){
		tc = 0;
		root = init_tree(1, n + 1);
		return 0;
	}
};

st tree;

int read(){
	int x = 0;
	int flag = 1;
	char ch = getchar();
	while('0' > ch or ch > '9'){
		if(ch == '-'){
			flag = -1;
		}
		ch = getchar();
	}
	
	while('0' <= ch and ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	
	x *= flag;
	
	return x;
}

int solve(){
	int i, j;
	int x, y;
	long long ans = 1e15;
	int cnt = 0;
	
	for(i=1;i<=n;i++){
		f[i].first = a[i];
		f[i].second.first = f[i].second.second = i;
	}
	
	for(i=1;i<n;i++){
		f[n + i].first = a[i] + a[i + 1];
		f[n + i].second.first = i;
		f[n + i].second.second = i + 1;
	}
	
	sort(f + 1, f + 2 * n);
	
	for(i=1;i<=n;i++){
		b[i][0] = b[i][1] = false;
	}
	
	tree.init();
	
	j = 0;
	
	while(!tree.query()){
		j++;
		x = f[j].second.first;
		y = f[j].second.second;
		
		b[x][y - x] = true;
		
		tree.update(x, y + 1);
	}
	
	for(i=1;i<2*n;i++){
		ans = min(ans, f[j].first - f[i].first);
		x = f[i].second.first;
		y = f[i].second.second;
		
		b[x][y - x] = false;
		tree.update(x, y + 1);
		
		while(!tree.query()){
			j++;
			if(j == 2 * n){
				printf("%lld\n", ans);
				return 0;
			}
			x = f[j].second.first;
			y = f[j].second.second;
		
			b[x][y - x] = true;
		
			tree.update(x, y + 1);
		}
	}
	
	return 0;
}

int main(){
	int i, j;
	
	t = read();
	
	while(t--){
		n = read();
		for(i=1;i<=n;i++){
			a[i] = read();
		}
		if(n == 1){
			printf("0\n");
		}else{
			solve();
		}
		
	}
	
	return 0;
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据