【集训队作业】IOI 2020 集训队作业 试题泛做 8

Codeforces 528C Data Center Drama

考虑存在解的必要条件,显然,各个点的度数应为偶数,且 M M 也应为偶数。

任意一张满足以上条件的图都存在一条长度为偶数的欧拉回路,取路上奇数位的边为正向,偶数位的边为反向即可构造一组解法。

因此,任意用最少的步数将图改造为满足以上条件的图即可。

时间复杂度 O ( N + M ) O(N+M)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
bool vis[MAXN];
unsigned cur[MAXN];
vector <int> ans, points;
vector <pair <int, int>> a[MAXN];
void work(int pos) {
	for (unsigned &i = cur[pos]; i < a[pos].size(); i++)
		if (!vis[a[pos][i].second]) {
			vis[a[pos][i].second] = true;
			work(a[pos][i].first);
		}
	ans.push_back(pos);
}
int main() {
	int n, m; read(n), read(m);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		a[x].emplace_back(y, i);
		a[y].emplace_back(x, i);
	}
	vector <int> points;
	for (int i = 1; i <= n; i++)
		if (a[i].size() & 1) points.push_back(i);
	while (!points.empty()) {
		int x = points.back(); points.pop_back();
		int y = points.back(); points.pop_back(), m++;
		a[x].emplace_back(y, m);
		a[y].emplace_back(x, m);
	}
	if (m & 1) {
		m++;
		a[1].emplace_back(1, m);
		a[1].emplace_back(1, m);
	}
	printf("%d\n", m); work(1);
	for (int i = 1; i <= m; i++)
		if (i & 1) printf("%d %d\n", ans[i - 1], ans[i]);
		else printf("%d %d\n", ans[i], ans[i - 1]);
	return 0;
}

Codeforces 536D Tavas in Kansas

首先求出最短路,并离散化。

不难得到动态规划解法,记 d p i , j , 0 / 1 dp_{i,j,0/1} 表示仅考虑到 S S 距离在 i i 以上,到 T T 距离在 j j 以上的点,先手 / 后手玩家能够取到的最优分差,转移显然可以枚举下一步如何行动。

观察转移,用前缀和优化之即可。

时间复杂度 O ( N 2 + M L o g N ) O(N^2+MLogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e3 + 5;
const long long INF = 1e18;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
namespace ShortestPath {
	const ll INF = 1e18;
	const int MAXP = 1e6;
	struct edge {int dest, len; };
	int n; ll dist[MAXP];
	vector <edge> a[MAXP];
	set <pair <ll, int> > st;
	void addedge(int x, int y, int z) {
		a[x].push_back((edge) {y, z});
	}
	void init(int x) {
		n = x; st.clear();
		for (int i = 1; i <= n; i++) {
			dist[i] = INF;
			a[i].clear();
		}
	}
	void work(int s) {
		dist[s] = 0;
		for (int i = 1; i <= n; i++)
			if (s != i) dist[i] = INF;
		st.insert(make_pair(0, s));
		while (!st.empty()) {
			pair <ll, int> tmp = *st.begin();
			st.erase(tmp);
			for (unsigned i = 0; i < a[tmp.second].size(); i++) {
				int dest = a[tmp.second][i].dest;
				ll newlen = tmp.first + a[tmp.second][i].len;
				if (newlen < dist[dest]) {
					st.erase(make_pair(dist[dest], dest));
					dist[dest] = newlen;
					st.insert(make_pair(dist[dest], dest));
				}
			}
		}
	}
}
int n, m, s, t;
int p[MAXN], x[MAXN], y[MAXN];
int cnt[MAXN][MAXN]; ll sum[MAXN][MAXN];
ll dp[MAXN][MAXN][2], aux[MAXN][MAXN][2];
void makecor(ll *x, int *y) {
	set <ll> st; map <ll, int> res;
	for (int i = 1; i <= n; i++)
		st.insert(x[i]);
	int tot = 0;
	for (auto x : st) res[x] = ++tot;
	for (int i = 1; i <= n; i++)
		y[i] = res[x[i]];
}
int calcnt(int lx, int rx, int ly, int ry) {
	return cnt[rx][ry] - cnt[rx][ly - 1] - cnt[lx - 1][ry] + cnt[lx - 1][ly - 1];
}
ll calsum(int lx, int rx, int ly, int ry) {
	return sum[rx][ry] - sum[rx][ly - 1] - sum[lx - 1][ry] + sum[lx - 1][ly - 1];
}
void debug() {
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++)
			printf("(%4lld,%4lld) ", dp[i][j][0], dp[i][j][1]);
		printf("\n");
	}
}
int main() {
	read(n), read(m), read(s), read(t);
	for (int i = 1; i <= n; i++)
		read(p[i]);
	ShortestPath :: init(n);
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		read(x), read(y), read(z);
		ShortestPath :: addedge(x, y, z);
		ShortestPath :: addedge(y, x, z);
	}
	ShortestPath :: work(s);
	makecor(ShortestPath :: dist, x);
	ShortestPath :: work(t);
	makecor(ShortestPath :: dist, y);
	for (int i = 1; i <= n; i++) {
		cnt[x[i]][y[i]]++;
		sum[x[i]][y[i]] += p[i];
	}
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= n; j++) {
		cnt[i][j] += cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1];
		sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
	}
	for (int i = n; i >= 1; i--)
	for (int j = n; j >= 1; j--) {
		aux[i][j][0] = dp[i + 1][j][1] + sum[i][n] - sum[i][j - 1];
		if (i != n) chkmax(aux[i][j][0], aux[i + 1][j][0]);
		if (calcnt(i, i, j, n) == 0) dp[i][j][0] = dp[i + 1][j][0];
		else dp[i][j][0] = aux[i][j][0] + sum[i - 1][j - 1] - sum[i - 1][n];
		
		aux[i][j][1] = dp[i][j + 1][0] - sum[n][j] + sum[i - 1][j];
		if (j != n) chkmin(aux[i][j][1], aux[i][j + 1][1]);
		if (calcnt(i, n, j, j) == 0) dp[i][j][1] = dp[i][j + 1][1];
		else dp[i][j][1] = aux[i][j][1] - sum[i - 1][j - 1] + sum[n][j - 1];
	}
	//debug();
	if (dp[1][1][0] > 0) puts("Break a heart");
	else if (dp[1][1][0] == 0) puts("Flowers");
	else puts("Cry");
	return 0;
}

Codeforces 538G Berserk Robot

首先,令对于点 ( x , y ) (x,y) ,令 x = x + y , y = x y x=x+y,y=x-y
并令 R = ( + 1 , + 1 ) , U = ( + 1 , 1 ) , L = ( 1 , 1 ) , D = ( + 1 , 1 ) R=(+1,+1),U=(+1,-1),L=(-1,-1),D=(+1,-1)

旋转坐标系后,可以认为 x , y x,y 两维是分立的,我们只需要分别解决一维的问题就可以了。

x x 坐标为例,令 s i s_i 表示串中前 i i 个元素的前缀和,则每条限制可以被描述为 s t i % L = x i t i L s n s_{t_i\% L}=x_i-\lfloor\frac{t_i}{L}\rfloor s_n 。并且, s i s_i 需要额外满足 s i i   ( m o d 2 ) s_i\equiv i\ (\bmod 2)

那么,考虑相邻的两个有限制的 s i , s j   ( i < j ) s_i,s_j\ (i<j) ,则有不等式 s i s j j i |s_i-s_j|\leq j-i ,可以解出一个 s n s_n 的范围。取所有限制的并,任取一个限制内的 s n s_n 即可。

时间复杂度 O ( N + L ) O(N+L)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXL = 2e6 + 5;
const long long INF = 4e18;
typedef long long ll;
template <typename T> T abs(T x) {if (x <= 0) return -x; else return x; }
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
int n, l; bool vis[MAXL];
ll t[MAXN], x[MAXN], y[MAXN];
pair <ll, ll> px[MAXL], py[MAXL];
ll solve(pair <ll, ll> x, pair <ll, ll> y) {
	if ((y.first - x.first) % (y.second - x.second) == 0)
		return (y.first - x.first) / (y.second - x.second);
	puts("NO"), exit(0);
	return -1;
}
ll Ceil(ll x, ll y) {
	ll tmp = INF / y;
	return (x + tmp * y) / y - tmp + 1;
}
ll Round(ll x, ll y) {
	ll tmp = INF / y;
	return (x + tmp * y) / y - tmp;
}
pair <ll, ll> calc(pair <ll, ll> x, pair <ll, ll> y, ll k) {
	if (y.second == x.second) {
		ll delta = abs(x.first - y.first);
		if (delta <= k) return make_pair(-l, l);
		else return make_pair(INF, -INF);
	}
	if (y.second < x.second) swap(x, y);
	ll coef = y.second - x.second, delta = y.first - x.first;
	ll Min = delta - k, Max = delta + k; pair <ll, ll> ans;
	if (Min % coef == 0) ans.first = Min / coef;
	else ans.first = Ceil(Min, coef);
	if (Max % coef == 0) ans.second = Max / coef;
	else ans.second = Round(Max, coef);
	return ans;
}
int main() {
	read(n), read(l);
	pair <ll, ll> limx, limy;
	limx = limy = make_pair(-l, l);
	vis[0] = true;
	for (int i = 1; i <= n; i++) {
		read(t[i]), read(x[i]), read(y[i]);
		ll tx = x[i] + y[i], ty = x[i] - y[i];
		x[i] = tx, y[i] = ty;
		if (abs(x[i]) % 2 != t[i] % 2 || abs(y[i]) % 2 != t[i] % 2) {
			puts("NO");
			return 0;
		}
		ll Mod = t[i] % l, Div = t[i] / l;
		if (vis[Mod]) {
			chkmax(limx.first, solve(px[Mod], make_pair(x[i], Div)));
			chkmin(limx.second, solve(px[Mod], make_pair(x[i], Div)));
			chkmax(limy.first, solve(py[Mod], make_pair(y[i], Div)));
			chkmin(limy.second, solve(py[Mod], make_pair(y[i], Div)));
		} else {
			vis[Mod] = true;
			px[Mod] = make_pair(x[i], Div);
			py[Mod] = make_pair(y[i], Div);
		}
	}
	vis[l] = true, px[l] = py[l] = make_pair(0, -1);
	for (int i = 1, last = 0; i <= l; i++)
		if (vis[i]) {
			pair <ll, ll> tmp;
			tmp = calc(px[last], px[i], i - last);
			chkmax(limx.first, tmp.first);
			chkmin(limx.second, tmp.second);
			tmp = calc(py[last], py[i], i - last);
			chkmax(limy.first, tmp.first);
			chkmin(limy.second, tmp.second);
			last = i;
		}
	if (abs(limx.first) % 2 != l % 2) limx.first++;
	if (abs(limy.first) % 2 != l % 2) limy.first++;
	if (limx.first > limx.second || limy.first > limy.second) {
		puts("NO");
		return 0;
	}
	ll sumx = limx.first, sumy = limy.first;
	static bool ansx[MAXL], ansy[MAXL];
	for (int i = 1, last = 0; i <= l; i++)
		if (vis[i]) {
			ll now, goal;
			now = px[last].first - px[last].second * sumx;
			goal = px[i].first - px[i].second * sumx;
			for (int j = last + 1; j <= i; j++)
				if (now <= goal) {
					ansx[j] = true;
					now++;
				} else now--;
			assert(now == goal);
			now = py[last].first - py[last].second * sumy;
			goal = py[i].first - py[i].second * sumy;
			for (int j = last + 1; j <= i; j++)
				if (now <= goal) {
					ansy[j] = true;
					now++;
				} else now--;
			assert(now == goal);
			last = i;
		}
	for (int i = 1; i <= l; i++) {
		if (ansx[i]) {
			if (ansy[i]) putchar('R');
			else putchar('U');
		} else {
			if (ansy[i]) putchar('D');
			else putchar('L');
		}
	}
	puts("");
	return 0;
}

Codeforces 538H Summer Dichotomy

首先,若给定的图不是二分图,则显然无解。

考虑图是二分图的情况,每个联通块可以用其两侧点对应区间的交来描述。

考虑一个划分方案,使得 1 1 集合内区间的并为 [ l 1 , r 1 ] [l_1,r_1] 2 2 集合内区间的并为 [ l 2 , r 2 ] [l_2,r_2] ,并且存在一个合法方案。那么,一定存在另一个合法方案使得 n 1 n_1 l 1 , l 2 l_1,l_2 中的一者,且 n 2 n_2 r 1 , r 2 , T n 1 r_1,r_2,T-n_1 中的一者,或者 n 1 n_1 是 $r_1,r_2 $ 中的一者,且 n 2 n_2 l 1 , l 2 , t n 1 l_1,l_2,t-n_1 中的一者。

枚举这两种情况,以第一种为例。

对所有区间按照左端点排序,并枚举 n 1 n_1 ,考虑如何计算 n 2 n_2

左端点在 n 1 n_1 右侧的区间必须分在 2 2 集合内,因此不能包含同一连通块对应的所有区间。令 M r M_r 表示这些区间的右端点最小值, M l M_l 表示这些区间的左端点最大值。

左端点在 n 1 n_1 左侧或相同的区间必须覆盖 n 1 n_1 ,若同一连通块对应的所有区间左端点均在 n 1 n_1 左侧或与 n 1 n_1 相同,应取右端点较大者划入 2 2 集合,令 M x M_x 这些区间的右端点最小值。

若存在合法的 ( n 1 , n 2 ) (n_1,n_2) n 2 n_2 应当取 T n 1 , M x , M r T-n_1,M_x,M_r 的最小值,且应 M l \geq M_l

实现可以参考以下代码,时间复杂度 O ( N L o g N + M ) O(NLogN+M)

在 Codeforces 的讨论版中存在一种较为简便的做法:

不考虑 t , T t,T 的限制,则显然应取 n 1 = max ( l i ) , n 2 = min ( r i ) n_1=\max(l_i),n_2=\min(r_i) ,判断 ( n 1 , n 2 ) (n_1,n_2) 是否合法即可。

t > n 1 + n 2 t>n_1+n_2 ,则令 n 1 = n 1 + ( t n 1 n 2 ) n_1=n_1+(t-n_1-n_2)
n 1 + n 2 > T n_1+n_2>T ,则令 n 2 = n 2 ( n 1 + n 2 T ) n_2=n_2-(n_1+n_2-T) ,然后判断 ( n 1 , n 2 ) (n_1,n_2) 是否合法即可。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF  = 1e9 + 7;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
int n, m, tot, ans[MAXN];
bool vis[MAXN], col[MAXN];
vector <int> e[MAXN], points[MAXN];
pair <int, int> s, tmp[2], v[MAXN], a[MAXN][2];
void foundAns(int x, int y) {
	assert(s.first <= x + y && x + y <= s.second);
	puts("POSSIBLE");
	printf("%d %d\n", x, y);
	static int ans[MAXN];
	for (int i = 1; i <= tot; i++) {
		if (a[i][0].first <= x && x <= a[i][0].second && a[i][1].first <= y && y <= a[i][1].second) {
			for (auto x : points[i])
				ans[x] = col[x];
		} else if (a[i][0].first <= y && y <= a[i][0].second && a[i][1].first <= x && x <= a[i][1].second) {
			for (auto x : points[i])
				ans[x] = !col[x];
		} else assert(false);
	}
	for (int i = 1; i <= n; i++)
		printf("%d", ans[i] + 1);
	printf("\n");
	exit(0);
}
void solveMin() {
	static pair <pair <int, int>, int> b[MAXN];
	for (int i = 1, j = 0; i <= n; i++) {
		b[++j] = make_pair(a[i][0], i);
		b[++j] = make_pair(a[i][1], i);
	}
	sort(b + 1, b + 2 * tot + 1);
	
	static int pre[MAXN], suf[MAXN];
	pre[0] = suf[2 * tot + 1] = INF;
	for (int i = 2 * tot; i >= 1; i--)
		suf[i] = min(suf[i + 1], b[i].first.second);
	for (int i = 1; i <= 2 * tot; i++)
		pre[i] = min(pre[i - 1], b[i].first.second);
	
	static int cnt[MAXN];
	priority_queue <int, vector <int>, greater <int>> Heap, Delt;
	for (int i = 1; i <= tot; i++)
		Heap.push(max(a[i][0].second, a[i][1].second));
	for (int i = 2 * tot, nxt; i >= 1; i = nxt) {
		nxt = i; while (nxt >= 1 && b[nxt].first.first == b[i].first.first) nxt--;
		while (!Heap.empty() && !Delt.empty() && Heap.top() == Delt.top()) {
			Heap.pop();
			Delt.pop();
		}
		int x = b[i].first.first, y = s.second - x;
		if (!Heap.empty()) chkmin(y, Heap.top());
		chkmin(y, suf[i + 1]);
		if (b[i].first.first <= pre[i] && y >= b[2 * tot].first.first && x + y >= s.first) foundAns(x, y);
		for (int j = nxt + 1; j <= i; j++) {
			if (++cnt[b[j].second] == 2) return;
			else Delt.push(max(a[b[j].second][0].second, a[b[j].second][1].second));
		}
	}
}
bool cmp(pair <pair <int, int>, int> x, pair <pair <int, int>, int> y) {
	return x.first.second > y.first.second;
}
void solveMax() {
	static pair <pair <int, int>, int> b[MAXN];
	for (int i = 1, j = 0; i <= n; i++) {
		b[++j] = make_pair(a[i][0], i);
		b[++j] = make_pair(a[i][1], i);
	}
	sort(b + 1, b + 2 * tot + 1, cmp);
	
	static int pre[MAXN], suf[MAXN];
	pre[0] = suf[2 * tot + 1] = 0;
	for (int i = 2 * tot; i >= 1; i--)
		suf[i] = max(suf[i + 1], b[i].first.first);
	for (int i = 1; i <= 2 * tot; i++)
		pre[i] = max(pre[i - 1], b[i].first.first);
	
	static int cnt[MAXN];
	priority_queue <int> Heap, Delt;
	for (int i = 1; i <= tot; i++)
		Heap.push(min(a[i][0].first, a[i][1].first));
	for (int i = 2 * tot, nxt; i >= 1; i = nxt) {
		nxt = i; while (nxt >= 1 && b[nxt].first.second == b[i].first.second) nxt--;
		while (!Heap.empty() && !Delt.empty() && Heap.top() == Delt.top()) {
			Heap.pop();
			Delt.pop();
		}
		int x = b[i].first.second, y = s.first - x;
		if (!Heap.empty()) chkmax(y, Heap.top());
		chkmax(y, suf[i + 1]);
		if (b[i].first.second >= pre[i] && y <= b[2 * tot].first.second && x + y <= s.second) foundAns(x, y);
		for (int j = nxt + 1; j <= i; j++) {
			if (++cnt[b[j].second] == 2) return;
			else Delt.push(min(a[b[j].second][0].first, a[b[j].second][1].first));
		}
	}
}
void dfs(int pos) {
	vis[pos] = true;
	points[tot].push_back(pos);
	chkmax(tmp[col[pos]].first, v[pos].first);
	chkmin(tmp[col[pos]].second, v[pos].second);
	for (auto x : e[pos])
		if (!vis[x]) {
			col[x] = !col[pos];
			dfs(x);
		} else if (col[pos] == col[x]) {
			puts("IMPOSSIBLE");
			exit(0);
		}
}
int main() {
	read(s.first), read(s.second), read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(v[i].first), read(v[i].second);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		e[x].push_back(y);
		e[y].push_back(x);
	}
	for (int i = 1; i <= n; i++) {
		if (!vis[i]) {
			tmp[0] = tmp[1] = make_pair(0, INF);
			tot++, dfs(i);
			if (tmp[0].first > tmp[0].second || tmp[1].first > tmp[1].second) {
				puts("IMPOSSIBLE");
				return 0;
			}
			a[tot][0] = tmp[0];
			a[tot][1] = tmp[1];
		}
	}
	solveMin();
	solveMax();
	puts("IMPOSSIBLE");
	return 0;
}

Codeforces 547D Mike and Fish

首先,若对于解的存在性不加证明,则显然可以用有上下界的网络流解题,时间复杂度 O ( N N ) O(N\sqrt{N})

考虑解的存在性,首先假设图中每行每列均有偶数个点,那么,在这张二分图上每个联通块求欧拉回路即可构造出一组可行的解。

对于不满足每行每列均有偶数个点的情况,在度为奇数的点间两两连边,转化为以上情况即可。

时间复杂度 O ( N L o g N ) O(NLogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
vector <pair <int, int>> a[MAXN];
int n, m, x[MAXN], y[MAXN]; unsigned cur[MAXN];
set <pair <int, int>> path;
bool vis[MAXN];
void work(int pos, int fa) {
	for (unsigned &i = cur[pos]; i < a[pos].size(); i++)
		if (!vis[a[pos][i].second]) {
			vis[a[pos][i].second] = true;
			work(a[pos][i].first, pos);
		}
	if (fa) path.insert(make_pair(fa, pos));
}
int main() {
	n = 2e5, read(m);
	for (int i = 1; i <= m; i++) {
		read(x[i]), read(y[i]), y[i] += n;
		a[x[i]].emplace_back(y[i], i);
		a[y[i]].emplace_back(x[i], i);
	}
	vector <int> odd;
	for (int i = 1; i <= 2 * n; i++)
		if (a[i].size() & 1) odd.push_back(i);
	int tot = m;
	while (!odd.empty()) {
		int x = odd.back(); odd.pop_back();
		int y = odd.back(); odd.pop_back();
		a[x].emplace_back(y, ++tot);
		a[y].emplace_back(x, tot);
	}
	for (int i = 1; i <= 2 * n; i++)
		work(i, 0);
	for (int i = 1; i <= m; i++)
		if (path.count(make_pair(x[i], y[i]))) putchar('b');
		else putchar('r');
	putchar('\n');
	return 0;
}

Codeforces 547E Mike and Friends

离线询问,在后缀树上线段树合并即可。

时间复杂度 O ( s i L o g N + Q L o g N ) O(\sum|s_i|LogN+QLogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5 + 5;
const int MAXQ = 5e5 + 5;
const int MAXP = 8e6 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct SegmentTreeMerging {
	struct Node {
		int lc, rc, sum;
		bool leaf;
	} a[MAXP];
	int size, n;
	void init(int x) {
		n = x;
		size = 0;
	}
	void update(int root) {
		a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum;
	}
	void modify(int &root, int l, int r, int pos, int d) {
		if (root == 0) root = ++size;
		if (l == r) {
			a[root].sum += d;
			a[root].leaf = true;
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) modify(a[root].lc, l, mid, pos, d);
		else modify(a[root].rc, mid + 1, r, pos, d);
		update(root);
	}
	void modify(int &root, int val, int d) {
		modify(root, 1, n, val, d);
	}
	int merge(int x, int y) {
		if (x == 0 || y == 0) return x + y;
		if (a[x].leaf) {
			a[x].sum += a[y].sum;
			return x;
		}
		a[x].lc = merge(a[x].lc, a[y].lc);
		a[x].rc = merge(a[x].rc, a[y].rc);
		update(x);
		return x;
	}
	void join(int &to, int from) {
		to = merge(to, from);
	}
	int query(int root, int l, int r, int ql, int qr) {
		if (root == 0) return 0;
		if (l == ql && r == qr) return a[root].sum;
		int mid = (l + r) / 2, ans = 0;
		if (mid >= ql) ans += query(a[root].lc, l, mid, ql, min(mid, qr));
		if (mid + 1 <= qr) ans += query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans;
	}
	int query(int root, int l, int r) {
		return query(root, 1, n, l, r);
	}
} ST;
struct SuffixAutomaton {
	struct Node {
		int child[26];
		int father, depth;
	} a[MAXN];
	vector <int> e[MAXN];
	vector <int> home[MAXN];
	vector <pair <int, int>> s[MAXN];
	int n, root, size, last;
	int newnode(int dep) {
		a[++size].depth = dep;
		return size;
	}
	void init() {
		size = 0;
		root = last = 0;
	}
	void extend(int ch) {
		int p = last, np;
		if (a[p].child[ch]) {
			int q = a[p].child[ch];
			if (a[p].depth + 1 == a[q].depth) np = q;
			else {
				np = newnode(a[p].depth + 1);
				memcpy(a[np].child, a[q].child, sizeof(a[q].child));
				a[np].father = a[q].father;
				a[q].father = np;
				while (a[p].child[ch] == q) {
					a[p].child[ch] = np;
					p = a[p].father;
				}
			}
		} else {
			np = newnode(a[p].depth + 1);
			while (a[p].child[ch] == 0) {
				a[p].child[ch] = np;
				p = a[p].father;
			}
			if (a[p].child[ch] == np) a[np].father = root;
			else {
				int q = a[p].child[ch];
				if (a[q].depth == a[p].depth + 1) a[np].father = q;
				else {
					int nq = newnode(a[p].depth + 1);
					memcpy(a[nq].child, a[q].child, sizeof(a[q].child));
					a[nq].father = a[q].father;
					a[q].father = a[np].father = nq;
					while (a[p].child[ch] == q) {
						a[p].child[ch] = nq;
						p = a[p].father;
					}
				}
			}
		}
		last = np;
	}
	void insert(char *s) {
		last = 0, n++;
		int len = strlen(s + 1);
		for (int i = 1; i <= len; i++) {
			extend(s[i] - 'a');
			home[n].push_back(last);
		}
	}
	int rt[MAXN], ans[MAXQ];
	vector <pair <pair <int, int>, int>> q[MAXN];
	void work(int pos) {
		for (auto x : s[pos])
			ST.modify(rt[pos], x.first, x.second);
		for (auto x : e[pos]) {
			work(x);
			ST.join(rt[pos], rt[x]);
		}
		for (auto x : q[pos])
			ans[x.second] = ST.query(rt[pos], x.first.first, x.first.second);
	}
	void solve(int m) {
		for (int i = 1; i <= size; i++)
			e[a[i].father].push_back(i);
		for (int i = 1; i <= n; i++)
		for (unsigned j = 0; j < home[i].size(); j++)
			s[home[i][j]].emplace_back(i, 1);
		work(0);
		for (int i = 1; i <= m; i++)
			writeln(ans[i]);
	}
} SAM;
char s[MAXN];
int main() {
	int n, m; read(n), read(m);
	SAM.init(), ST.init(n);
	for (int i = 1; i <= n; i++) {
		scanf("\n%s", s + 1);
		SAM.insert(s);
	}
	for (int i = 1; i <= m; i++) {
		int l, r, k; read(l), read(r), read(k);
		SAM.q[SAM.home[k].back()].emplace_back(make_pair(l, r), i);
	}
	SAM.solve(m);
	return 0;
}

Codeforces 549E Sasha Circle

建议参考 官方题解 阅读。

A A 为圆内的点集, B B 为圆外的点集。

可以认为,当存在一个圆,使得 A A 中的点都在圆内或圆上, B B 中的点都在圆外时,题目有解

A A 中只有一个点,显然有解;若 A > 1 |A|>1 ,容易看出如果题目有解,那么肯定存在一个解使得 A A 中至少有两个点在圆上。

考虑枚举 A A 中的两个点,那么圆心一定在两点连线的中垂线上。对于其他 A A 中的点和 B B 中的点,每个点必须在圆内或者必须在圆外,那么会得到一个圆心坐标的可行区间。

由此,可以得到一个时间复杂度为 O ( ( N 2 + M 2 ) ( N + M ) ) O((N^2+M^2)(N+M)) 的算法。

考虑转化问题。

将原平面作为 z = 0 z=0 平面,将问题放到三维坐标系中讨论,画出抛物面 z = x 2 + y 2 z=x^2+y^2

考虑抛物面和一个不与 z = 0 z=0 垂直的平面相交得到的截面在 z = 0 z=0 上的投影。

假设平面为 a x + b y + z = c ax+by+z=c ,那么 x 2 + a x + y 2 + b y = c x^2+ax+y^2+by=c

( x + a 2 ) 2 + ( y + b 2 ) 2 = c + a 2 + b 2 4 (x+\frac{a}{2})^2+(y+\frac{b}{2})^2=c+\frac{a^2+b^2}{4}

通过方程可以看出投影一定是一个圆。

那么考虑将 z = 0 z=0 上的点 ( x , y , 0 ) (x,y,0) 映射到 ( x , y , x 2 + y 2 ) (x,y,x^2+y^2) ,这是一一对应的。

也就是说 z = 0 z=0 上的一个圆对应一个抛物面的截面,容易发现圆内的点一定在对应平面下方,圆外的点一定在对应平面上方,圆上的点一定在对应平面上。

于是问题转化为求一个平面,将抛物面上两点集分开,使得一点集在平面上方,另一点集在平面下方或平面上。

类比于平面上的情况,可以知道如果有解,则一定存在一个答案平面经过下方点集的上凸壳上两点,容易证明这两点一定在上凸壳上相邻,即对应上凸壳的一条边。

然后考虑上凸壳的边在 z = 0 z=0 上的正投影,容易发现投影会是平面上对应凸包的一个剖分,当不存在四点共圆时,形成三角剖分,否则将其补成三角剖分。

由之前的讨论可知,需要枚举的点对就只有原凸包上的相邻点和剖分中的边的两端点。

考虑这个剖分的特征,回到凸壳上考虑,对于凸壳的每一个面,凸壳必然在其所在平面下面,对应到平面上就是凸包必然在三角剖分后每个三角形的外接圆内部。

所以只需要找到一个这样的剖分,需要枚举的边数就降到凸包的点数级别了。

考虑求出一个这样的剖分,考虑分治,在凸包上按逆(顺)时针讨论, s o l v e ( l , r ) solve(l,r) 表示将点 l l 到点 r r 剖分。

( l , r ) (l,r) 这条边作为底边,找到一个点 k , ( l < k < r ) k,(l<k<r) ,使得这三个点构成三角形的外接圆最大,可以证明这个三角形会包含整个凸包,然后递归做 s o l v e ( l , k ) , s o l v e ( k , r ) solve(l,k),solve(k,r) 就好了。

时间复杂度 O ( ( N + M ) V 2 3 + N L o g N + M L o g M ) O((N+M)V^{\frac{2}{3}}+NLogN+MLogM)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const double eps = 1e-8;
const double INF = 1e99;
const double pi = acos(-1);
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
struct point {double x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, int b) {return (point) {a.x * b, a.y * b}; }
double operator * (point a, point b) {return a.x * b.y - a.y * b.x; }
double dot(point a, point b) {return a.x * b.x + a.y * b.y; }
double dist(point a, point b) {
	a = a - b;
	return sqrt(a.x * a.x + a.y * a.y);
}
bool onseg(point a, point b, point c) {
	return fabs(dist(a, b) - dist(a, c) - dist(b, c)) <= eps;
}
int n, m, top;
point p, a[MAXN], b[MAXN], c[MAXN];
vector <pair <int, int>> e;
bool cmp(point a, point b) {
	if ((a - p) * (b - p) == 0) return dist(a, p) < dist(b, p);
	else return (a - p) * (b - p) > 0;
}
void convexHull() {
	for (int i = 1; i <= n; i++)
		if (a[i].y < a[1].y || (a[i].y == a[1].y && a[i].x < a[1].x)) swap(a[i], a[1]);
	p = c[top = 1] = a[1];
	sort(a + 2, a + n + 1, cmp), a[n + 1] = p;
	for (int i = 2; i <= n + 1; i++) {
		while (top >= 2 + (i == n + 1) && (a[i] - c[top - 1]) * (c[top] - c[top - 1]) >= 0) top--;
		c[++top] = a[i];
	}
}
void work(int l, int r) {
	e.emplace_back(l, r);
	if (l == r - 1) return;
	double Max = -INF; int pos = 0;
	for (int i = l + 1; i <= r - 1; i++) {
		double ang = dot(c[r] - c[i], c[l] - c[i]) / ((c[r] - c[i]) * (c[l] - c[i]));
		if (ang > Max) {
			Max = ang;
			pos = i;
		}
	}
	work(l, pos);
	work(pos, r);
}
bool check(point l, point r) {
	pair <double, double> rng = make_pair(-INF, INF);
	for (int i = 1; i <= n; i++) {
		if (fabs((a[i] - l) * (r - l)) <= eps) {
			assert(onseg(l, r, a[i]));
			continue;
		}
		if ((a[i] - l) * (r - l) > 0) {
			double ang = dot(r - a[i], l - a[i]) / ((r - a[i]) * (l - a[i]));
			chkmax(rng.first, ang);
		} else {
			double ang = dot(l - a[i], r - a[i]) / ((l - a[i]) * (r - a[i]));
			chkmin(rng.second, -ang);
		}
	}
	for (int i = 1; i <= m; i++) {
		if (fabs((b[i] - l) * (r - l)) <= eps) {
			if (onseg(l, r, b[i])) return false;
			continue;
		}
		if ((b[i] - l) * (r - l) > 0) {
			double ang = dot(r - b[i], l - b[i]) / ((r - b[i]) * (l - b[i]));
			chkmin(rng.second, ang);
		} else {
			double ang = dot(l - b[i], r - b[i]) / ((l - b[i]) * (r - b[i]));
			chkmax(rng.first, -ang);
		}
	}
	return rng.first < rng.second - eps;
}
bool solve() {
	if (n == 1) return true;
	convexHull();
	e.clear(), work(1, top - 1);
	for (auto x : e)
		if (check(c[x.first], c[x.second])) return true;
	return false;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(a[i].x), read(a[i].y);
	for (int i = 1; i <= m; i++)
		read(b[i].x), read(b[i].y);
	if (solve()) {
		puts("YES");
		return 0;
	}
	swap(n, m);
	swap(a, b);
	if (solve()) {
		puts("YES");
		return 0;
	}
	puts("NO");
	return 0;
}

Codeforces 553E Kyoya and Train

考虑一个暴力 dp ,记 d p i , j dp_{i,j} 表示在点 i i 处,时刻 j j 最优决策的期望花费。

则有
d p i , j = { x + d i s t ( i , N ) j > T 0 i = N , j T M i n i e E { c o s t i , e + k = 1 t p i , e , k d p e , j + k } i N , j T dp_{i,j}=\left\{\begin{array}{rcl}x+dist(i,N)&&{j>T}\\0&&{i=N,j≤T}\\Min_{i\Rightarrow e\in E}\{cost_{i,e}+\sum_{k=1}^{t}p_{i,e,k}*dp_{e,j+k}\}&&{i\ne N,j≤T}\end{array} \right.

我们希望求出 d p 1 , 0 dp_{1,0}

用分治 FFT 优化该 dp 即可。

具体来说,我们可以轻松地得到 d p , j   ( j > T ) dp_{*,j}\ (j>T) ,也可以快速地计算出 d p , j   ( j > T ) dp_{*,j}\ (j>T) 对后续 dp 的影响,即 t r a n s i , e , j = k = 1 t [ j + k > T ] p i , e , k d p e , j + k trans_{i,e,j}=\sum_{k=1}^{t}[j+k>T]*p_{i,e,k}*dp_{e,j+k}

那么考虑分治,对于时刻区间 [ l , r ] [l,r] ,取 m i d = l + r 2 mid=\lfloor\frac{l+r}{2}\rfloor ,首先计算出时刻区间 [ m i d + 1 , r ] [mid+1,r] 的 dp 值,然后用 FFT 计算时刻区间 [ m i d + 1 , r ] [mid+1,r] 的 dp 值对时刻区间 [ l , m i d ] [l,mid] 的影响,即 t r a n s i , e , j = k = 1 t [ m i d < j + k r ] p i , e , k d p e , j + k trans_{i,e,j}=\sum_{k=1}^{t}[mid<j+k≤r]*p_{i,e,k}*dp_{e,j+k} ,最后递归计算时刻区间 [ l , m i d ] [l,mid] 的 dp 值。

时间复杂度 O ( N 3 + M T L o g 2 T ) O(N^3+MTLog^2T)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
const int MAXM = 32768;
const double INF = 1e18;
const double pi = acos(-1);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct point {double x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, point b) {return (point) {a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; }
point operator / (point a, double x) {return (point) {a.x / x, a.y / x}; }
int n, m, t, fine;
vector <pair <int, int> > e;
double dist[MAXN][MAXN], edge[MAXN][MAXN];
vector <double> dp[MAXN], trans[MAXN][MAXN], p[MAXN][MAXN];
int N, Log, home[MAXM]; point a[MAXM], b[MAXM], c[MAXM];
void FFT(point *a, int mode) {
	for (int i = 0; i < N; i++)
		if (home[i] > i) swap(a[i], a[home[i]]);
	for (int len = 2; len <= N; len <<= 1) {
		point delta = (point) {cos(2 * pi / len * mode), sin(2 * pi / len * mode)};
		for (int i = 0; i < N; i += len) {
			point now = (point) {1, 0};
			for (int j = i, k = i + len / 2; k < i + len; j++, k++) {
				point tmp = a[j], tnp = a[k];
				a[j] = tmp + tnp * now;
				a[k] = tmp - tnp * now;
				now = now * delta;
			}
		}
	}
	if (mode == -1) {
		for (int i = 0; i < N; i++)
			a[i] = a[i] / N;
	}
}
void transfer(int l, int mid, int r, vector <double> &dp, vector <double> &p, vector <double> &res) {
	N = 1, Log = 0;
	while (N <= (r - l) + (r - mid)) {
		N <<= 1;
		Log++;
	}
	for (int i = 0; i < N; i++) {
		int tmp = i, res = 0;
		for (int j = 1; j <= Log; j++) {
			res <<= 1;
			res += tmp & 1;
			tmp >>= 1;
		}
		home[i] = res;
	}
	for (int i = 0; i < N; i++)
		a[i] = b[i] = (point) {0, 0};
	for (int i = mid + 1; i <= r; i++)
		a[r - i].x = dp[i];
	for (int i = 0; i <= r - l; i++)
		b[i].x = p[i];
	FFT(a, 1), FFT(b, 1);
	for (int i = 0; i < N; i++)
		c[i] = a[i] * b[i];
	FFT(c, -1);
	for (int i = l; i <= mid; i++)
		res[i] += c[r - i].x;
}
void work(int l, int r) {
	if (l == r) {
		for (int i = 1; i <= n - 1; i++)
			dp[i][l] = INF;
		for (auto x : e) {
			int i = x.first, j = x.second;
			chkmin(dp[i][l], trans[i][j][l] + edge[i][j]);
		}
		return;
	}
	int mid = (l + r) / 2;
	work(mid + 1, r);
	for (auto x : e) {
		int i = x.first, j = x.second;
		transfer(l, mid, r, dp[j], p[i][j], trans[i][j]);
	}
	work(l, mid);
}
int main() {
	read(n), read(m), read(t), read(fine);
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= n; j++)
		if (i == j) dist[i][j] = 0;
		else dist[i][j] = INF;
	for (int i = 1; i <= n; i++)
		dp[i].resize(t + 1);
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		read(x), read(y), read(z);
		if (dist[x][y] == INF) {
			e.emplace_back(x, y);
			edge[x][y] = z;
		}
		chkmin(dist[x][y], z * 1.0);
		chkmin(edge[x][y], z * 1.0);
		p[x][y].push_back(0);
		trans[x][y].resize(t + 1);
		for (int j = 1; j <= t; j++) {
			int val; read(val);
			p[x][y].push_back(val * 0.00001);
		}
	}
	for (int k = 1; k <= n; k++)
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= n; j++)
		chkmin(dist[i][j], dist[i][k] + dist[k][j]);
	for (auto x : e) {
		int i = x.first, j = x.second;
		double res = 1;
		for (int k = t; k >= 0; k--) {
			res -= p[i][j][t - k];
			trans[i][j][k] = res * (dist[j][n] + fine);
		}
	}
	work(0, t);
	printf("%.10lf\n", dp[1][0]);
	return 0;
}

Codeforces 555E Case of Computer Network

首先,可以将树上的边双连通分量缩成点,因为显然可以定向使得边双连通分量强连通。

此后,问题变成了森林上的问题,树上差分解决即可。

时间复杂度 O ( N L o g N + M + Q L o g N ) O(NLogN+M+QLogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXLOG = 20;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
vector <pair <int, int>> a[MAXN];
int n, m, q, top, tot, Stack[MAXN], belong[MAXN];
int timer, x[MAXN], y[MAXN], dfn[MAXN], low[MAXN], col[MAXN];
void work(int pos, int fa, int c) {
	Stack[++top] = pos, col[pos] = c;
	dfn[pos] = low[pos] = ++timer;
	for (auto x : a[pos])
		if (dfn[x.first] == 0) {
			work(x.first, x.second, c);
			chkmin(low[pos], low[x.first]);
		} else if (x.second != fa) chkmin(low[pos], dfn[x.first]);
	if (dfn[pos] == low[pos]) {
		int tmp = Stack[top--];
		belong[tmp] = ++tot;
		while (tmp != pos) {
			tmp = Stack[top--];
			belong[tmp] = tot;
		}
	}
}
vector <int> b[MAXN];
int depth[MAXN], father[MAXN][MAXLOG];
void dfs(int pos, int fa) {
	father[pos][0] = fa;
	depth[pos] = depth[fa] + 1;
	for (int i = 1; i < MAXLOG; i++)
		father[pos][i] = father[father[pos][i - 1]][i - 1];
	for (unsigned i = 0; i < b[pos].size(); i++)
		if (b[pos][i] != fa) dfs(b[pos][i], pos);
}
int lca(int x, int y) {
	if (depth[x] < depth[y]) swap(x, y);
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (depth[father[x][i]] >= depth[y]) x = father[x][i];
	if (x == y) return x;
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (father[x][i] != father[y][i]) {
			x = father[x][i];
			y = father[y][i];
		}
	return father[x][0];
}
bool vis[MAXN];
int sum[MAXN][2];
void getans(int pos, int fa) {
	vis[pos] = true;
	for (auto x : b[pos])
		if (x != fa) {
			getans(x, pos);
			sum[pos][0] += sum[x][0];
			sum[pos][1] += sum[x][1];
		}
	if (sum[pos][0] && sum[pos][1]) {
		puts("No");
		exit(0);
	}
}
int main() {
	read(n), read(m), read(q);
	for (int i = 1; i <= m; i++) {
		read(x[i]), read(y[i]);
		a[x[i]].emplace_back(y[i], i);
		a[y[i]].emplace_back(x[i], i);
	}
	for (int i = 1, j = 0; i <= n; i++)
		if (dfn[i] == 0) work(i, 0, ++j);
	for (int i = 1; i <= m; i++)
		if (belong[x[i]] != belong[y[i]]) {
			b[belong[x[i]]].push_back(belong[y[i]]);
			b[belong[y[i]]].push_back(belong[x[i]]);
		}
	for (int i = 1; i <= tot; i++)
		if (depth[i] == 0) dfs(i, 0);
	for (int i = 1; i <= q; i++) {
		int x, y; read(x), read(y);
		if (col[x] != col[y]) {
			puts("No");
			return 0;
		}
		x = belong[x], y = belong[y];
		int z = lca(x, y);
		sum[x][0]++, sum[z][0]--;
		sum[y][1]++, sum[z][1]--;
	}
	for (int i = 1; i <= tot; i++)
		if (!vis[i]) getans(i, 0);
	puts("Yes");
	return 0;
}

Codeforces 559E Gerald and Path

考虑按照从左向右的顺序 DP 。

由于可能的分界点数是 O ( N ) O(N) 的,可以记 d p i dp_i 表示 a x a_x 在第 i i 个分界点 p o s i pos_i 左侧的灯塔照到的最右侧的位置是第 i i 个分界点时,最优的覆盖长度。

转移时考虑 O ( N 2 ) O(N^2) 枚举其右侧的下一个被完全覆盖的区间 [ p o s j , p o s k ] [pos_j,pos_k] ,判断其是否能够被完全覆盖,若可以,则将 d p i + p o s k p o s j dp_{i}+pos_k-pos_j 转移至 d p k dp_k

那么,剩余的问题就在于判断区间 [ p o s j , p o s k ] [pos_j,pos_k] 是否能够被完全覆盖。

枚举枚举左起第一盏照到 p o s j pos_j 的灯,其右侧的灯都应照向右侧,由此,可以将其递归为一个子问题。

以下代码的时间复杂度为 O ( N 4 ) O(N^4) ,可简单优化至 O ( N 3 ) O(N^3) ,但实际运行迅速。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
const int INF  = 5e8;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
pair <int, int> a[MAXN];
unordered_map <ll, bool> mp;
int n, dp[MAXN][2], c[MAXN][2][MAXN][2];
bool solve(int l, int r) {
	if (l >= r) return true;
	ll tmp = 1ll * INF * l + r;
	if (mp.count(tmp)) return mp[tmp];
	bool &ans = mp[tmp];
	ans = false; int Max = l;
	for (int i = 1; i <= n && !ans; i++)
		if (a[i].first > l && a[i].first < r) {
			if (a[i].first - a[i].second <= l) {
				int tmp = max(Max, a[i].first);
				for (int j = i + 1; j <= n && a[j].first <= tmp; j++)
					chkmax(tmp, a[j].first + a[j].second);
				ans |= solve(tmp, r);
			}
			chkmax(Max, a[i].first + a[i].second);
		}
	return ans;
}
int coef(int l, int tl, int r, int tr) {
	if (c[l][tl][r][tr] != -INF - 1) return c[l][tl][r][tr];
	int &ans = c[l][tl][r][tr]; ans = -INF;
	int bl = a[l].first - (tl == 0) * a[l].second, cl = a[l].first + (tl == 1) * a[l].second;
	int br = a[r].first - (tr == 0) * a[r].second, cr = a[r].first + (tr == 1) * a[r].second;
	if (bl > br || cl > cr || (l == r && tl != tr)) return ans;
	for (int i = 1; i <= n; i++)
		if (a[i].first >= bl && a[i].first <= cl && i != l) chkmax(cl, a[i].first + a[i].second);
	for (int i = n; i >= 1; i--)
		if (a[i].first <= cr && a[i].first >= br && i != r) chkmin(br, a[i].first - a[i].second);
	if (solve(cl, br)) return ans = cr - bl;
	return ans;
}
int main() {
	read(n);
	a[0] = make_pair(-INF, 0);
	for (int i = 1; i <= n; i++)
		read(a[i].first), read(a[i].second);
	sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= n; j++)
		c[i][0][j][0] = c[i][0][j][1] = c[i][1][j][0] = c[i][1][j][1] = -INF - 1;
	for (int i = 0; i <= n; i++) {
		int tmp = dp[i][0];
		for (int j = i + 1; j <= n; j++)
		for (int k = i + 1; k <= n; k++) {
			if (a[j].first > a[i].first) chkmax(dp[k][0], tmp + coef(j, 1, k, 0));
			if (a[j].first > a[i].first) chkmax(dp[k][1], tmp + coef(j, 1, k, 1));
			if (a[j].first - a[j].second > a[i].first) chkmax(dp[k][0], tmp + coef(j, 0, k, 0));
			if (a[j].first - a[j].second > a[i].first) chkmax(dp[k][1], tmp + coef(j, 0, k, 1));
		}
		tmp = dp[i][1];
		for (int j = i + 1; j <= n; j++)
		for (int k = i + 1; k <= n; k++) {
			if (a[j].first > a[i].first + a[i].second) chkmax(dp[k][0], tmp + coef(j, 1, k, 0));
			if (a[j].first > a[i].first + a[i].second) chkmax(dp[k][1], tmp + coef(j, 1, k, 1));
			if (a[j].first - a[j].second > a[i].first + a[i].second) chkmax(dp[k][0], tmp + coef(j, 0, k, 0));
			if (a[j].first - a[j].second > a[i].first + a[i].second) chkmax(dp[k][1], tmp + coef(j, 0, k, 1));
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		chkmax(ans, dp[i][0]);
		chkmax(ans, dp[i][1]);
	}
	cout << ans << endl;
	return 0;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/103709174