1. 程式人生 > 實用技巧 >#2466. 「POI2014」卡片 Card

#2466. 「POI2014」卡片 Card

將每個位置拆成兩個狀態 \(0/1\),分別表示該牌選擇正面/反面,如果 \(v_{i,a} \le v_{i + 1, b}\) 則在這兩個狀態間連邊,
最終就是看兩頭是否存在一條通路。

可以用線段樹來維護每段區間兩頭的四種情況是否可能連通即可。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define fi first
#define se second
const int N = 2e5 + 10, inf = 0x3f3f3f3f, mod = 1e9 + 7;
typedef pair <int, int> P;
typedef long long LL;

int n, m, a[N][2];
bool c[N<<2][2][2], t[N<<2];

inline void chk_(int o, int l, int r) {
	int mid = (l + r)>>1;
	c[o][0][0] = c[o][0][1] = c[o][1][0] = c[o][1][1] = t[o] = 0;
	if (!t[o<<1] || !t[o<<1|1])
		return;
	rep (i, 0, 1)
		rep (j, 0, 1)
		   	rep (x, 0, 1) 
		   		rep (y, 0, 1)
		   			if (a[mid][x] <= a[mid + 1][y])
		       			c[o][i][j] = max(c[o][i][j], c[o<<1][i][x] && c[o<<1|1][y][j]);
	t[o] = c[o][0][0] || c[o][0][1] || c[o][1][0] || c[o][1][1];
}

void build_(int o, int l, int r) {
	if (l == r) {
		c[o][0][0] = c[o][1][1] = t[o] = 1;
		return;
	}
	int mid = (l + r)>>1;
	build_(o<<1, l, mid), build_(o<<1|1, mid + 1, r);
	chk_(o, l, r);
}

void update_(int o, int l, int r, int x) {
	if (l == r)
		return;
	int mid = (l + r)>>1;
	if (x <= mid)
		update_(o<<1, l, mid, x);
	else 
		update_(o<<1|1, mid + 1, r, x);
	chk_(o, l, r);
}

int main() {
	scanf("%d", &n);
	rep (i, 1, n)
		scanf("%d%d", &a[i][0], &a[i][1]);
	build_(1, 1, n);
	scanf("%d", &m);
	while (m--) {
		int u, v;
		scanf("%d%d", &u, &v);
		swap(a[u], a[v]);
		update_(1, 1, n, u), update_(1, 1, n, v);
		printf("%s\n", t[1] ? "TAK" : "NIE");
	}
}