[TIOJ] 1852. 分眼皮

題目連結:http://tioj.infor.org/problems/1852
有趣的題目XD,看到24先猜砍半枚舉,但想了一段時間才知道該怎麼讓枚舉出來的兩個集合跟答案有關連。
首先發現,假設最後選出來的三個數字分別是$(a, b, c)$(不失一般性假設$a \geq b \geq c$),也就是說假設在枚舉出來的第一個集合中有一個三元組$(x, y, z)$(沒有大小關係),那只要在第二個集合中找到$(x^\prime, y^\prime, z^\prime)$使得$x+x^\prime \geq y+y^\prime \geq z+z^\prime$就好。
接著考慮$a-b, c-b$,發現只要$a-b\geq 0 \land c-b \leq 0$就可以代表$a \geq b \geq c$,也就是說對於第一個集合中的$(x, y, z)$,我們就是要找到使得$x+x^\prime-(y+y^\prime) \geq 0 \land z+z^\prime - (y+y^\prime) \leq 0$(也就是$y^\prime - x^\prime \leq x-y \land y^\prime - z^\prime \geq z-y$)成立的$(x^\prime, y^\prime, z^\prime)$中,$x^\prime - z^\prime$最小的那個,因為他就代表$(x, y, z)$可以配出的最小值($x+x^\prime - (z+z^\prime)$)。
這樣,就會發現問題轉換成給你一堆二維帶權資料點($(y^\prime - x^\prime, y^\prime - z^\prime)$, $x^\prime - z^\prime$),請求出第一維大於等於某個數且第二維小於等於某個數中,值最小的那個。這問題簡單的對一維排序後,加上一些前(後)綴極值資料結構,再利用雙指針就可以解決了
#include <bits/stdc++.h>
using namespace std;
typedef int64_t lld;
const int N = 24;
const lld INF = 1LL<<60;

class BIT{
	private:
		vector<lld> arr;
		inline int lowbit(int x){return x&(-x);}
	public:
		void init(int n){
			arr.resize(n);
			fill(arr.begin(), arr.end(), INF);
		}
		void modify(int p, lld x){
			while(p){
				arr[p] = min(arr[p], x);
				p -= lowbit(p);
			}
		}
		lld query(int p){
			lld ret = INF;
			while(p < (int)arr.size()){
				ret = min(ret, arr[p]);
				p += lowbit(p);
			}
			return ret;
		}
} bit;

class LiSan{
	private:
		vector<lld> vv;
	public:
		void init(){vv.clear();}
		void insert(lld a){vv.push_back(a);}
		void done(){
			sort(vv.begin(), vv.end());
			vv.resize(distance(vv.begin(), unique(vv.begin(), vv.end())));
		}
		int size(){return (int)vv.size();}
		int get(lld x){
			return distance(vv.begin(), lower_bound(vv.begin(), vv.end(), x));
		}
		lld inv_get(int x){return vv[x];}
} lisan;

struct Obj{
	lld a, b, c;
	Obj(lld _=0, lld __=0, lld ___=0): a(_), b(__), c(___){}
};

vector<Obj> A, B;
int arr[N];
void dfs1(int,int,lld,lld,lld);
void dfs2(int,int,lld,lld,lld);

int main(){
	ios_base::sync_with_stdio(0); cin.tie(0);
	int n; cin >> n;
	for(int i=0;i<n;i++) cin >> arr[i];
	A.reserve((int)(pow(3, n>>1)+0.5)); dfs1(0, n>>1, 0, 0, 0); 
	B.reserve((int)(pow(3, n-(n>>1))+0.5)); dfs2(n>>1, n, 0, 0, 0);
	lisan.done(); bit.init(lisan.size()+1);
	sort(A.begin(), A.end(), [](const Obj& a, const Obj& b){
		return a.c-a.b < b.c-b.b;
	});
	sort(B.begin(), B.end(), [](const Obj& a, const Obj& b){
		return a.b-a.c < b.b-b.c;
	});
	lld ans = INF; int p = 0;
	for(auto o : B){
		while(p < (int)A.size() and o.b-o.c >= A[p].c-A[p].b){
			bit.modify(lisan.get(A[p].a-A[p].b)+1, A[p].a-A[p].c);
			p++;
		}
		ans = min(ans, o.a-o.c+bit.query(lisan.get(o.b-o.a)+1));
	}
	cout << ans << '\n';
	return 0;
}

void dfs1(int i, int n, lld a, lld b, lld c){
	if(i == n){
		A.emplace_back(a, b, c);
		lisan.insert(a-b);
		return;
	}
	dfs1(i+1, n, a+arr[i], b, c);
	dfs1(i+1, n, a, b+arr[i], c);
	dfs1(i+1, n, a, b, c+arr[i]);
}

void dfs2(int i, int n, lld a, lld b, lld c){
	if(i == n){
		B.emplace_back(a, b, c);
		return;
	}
	dfs2(i+1, n, a+arr[i], b, c);
	dfs2(i+1, n, a, b+arr[i], c);
	dfs2(i+1, n, a, b, c+arr[i]);
}

留言

這個網誌中的熱門文章

[NPSC] 2009初賽 E. 檸檬汽水傳說

[TIOJ] 1902. 「殿仁.王,不認識,誰啊?」,然後他就死了……

[AtCoder] [經典競程 90 題] 024 - Select +/- One(★2)