← 全部文章

JOISC 2020 题解

本文目录(16 节)
  1. Day1
  2. ビルの飾りつけ 4 (Building 4)
  3. 美味しい美味しいハンバーグ (Hamburg Steak)
  4. 掃除 (Sweeping)
  5. Day2
  6. カメレオンの恋 (Chameleon's Love)
  7. ジョイッターで友だちをつくろう (Making Friends on Joitter is Fun)
  8. 最古の遺跡 3 (Ruins 3)
  9. Day3
  10. 星座 3 (Constellation 3)
  11. 収穫 (Harvest)
  12. 迷い猫 (Stray Cat)
  13. Day4
  14. 首都 (Capital City)
  15. 伝説の団子職人 (Legendary Dango Maker)(1,2,3,4,5,6)
  16. 治療計画 (Treatment Project)

JOISC 2020

Day1

ビルの飾りつけ 4 (Building 4)

先考虑 n≤2×103n \le 2 \times 10^3 怎么做。

设 fi,j,0/1f_{i,j,0/1} 表示考虑前 ii 个数,在 aa 中选了 jj 个数,第 ii 个数选的是 a/ba/b 是否可行。

那么 O(n2)\mathcal O(n^2) dp 即可,如果有方案,从后往前构造。

注意到对于 fi,j,0/1f_{i,j,0/1},如果 i,0/1i,0/1 固定,jj 可行的方案为一个区间。

因此只用 O(n)\mathcal O(n) dp 出对于每个 i,0/1i,0/1,fi,j,0/1f_{i,j,0/1} 中 jj 的可行区间。

const int N = 5e5 + 7, inf = 1e9;
int n, a[N<<1], b[N<<1];
pi f[N<<1][2];

inline void upd(pi &a, pi b, bool c) {
	a.fi = min(a.fi, b.fi + c), a.se = max(a.se, b.se + c);
}

inline bool pd(pi a, int x) {
	return a.fi <= x && a.se >= x;
}

int main() {
	rd(n), rda(a, n << 1), rda(b, n << 1);
	f[1][0] = mp(1, 1), f[1][1] = mp(0, 0);
	for (int i = 2; i <= (n << 1); i++)
		for (int j = 0; j < 2; j++)
			f[i][j] = mp(inf, -inf);
	for (int i = 1; i < (n << 1); i++)
		for (int j = 0; j < 2; j++)
			if (f[i][j] != mp(inf, -inf)) {
				if (j == 0 && a[i+1] >= a[i])
					upd(f[i+1][0], f[i][j], 1);
				if (j == 1 && a[i+1] >= b[i])
					upd(f[i+1][0], f[i][j], 1);
				if (j == 0 && b[i+1] >= a[i])
					upd(f[i+1][1], f[i][j], 0);
				if (j == 1 && b[i+1] >= b[i])
					upd(f[i+1][1], f[i][j], 0);
			}
	if (!pd(f[n<<1][0], n) && !pd(f[n<<1][1], n)) return print(-1), 0;
	int p = n, q = 0;
	string s;
	a[n<<1|1] = b[n<<1|1] = inf;
	for (int i = n << 1; i; i--)
		if (pd(f[i][0], p) && a[i] <= (q ? b[i+1] : a[i+1]))
			s += 'A', --p, q = 0;
		else s += 'B', q = 1;
	reverse(s.begin(), s.end());
	prints(s);
	return 0;
}

美味しい美味しいハンバーグ (Hamburg Steak)

ouuan 的做法,不知道为啥能过。(upd:现在似乎已经被卡掉了)

首先随机 kk 个矩形,然后依次将剩下的矩形与 kk 个矩形求交,哪个剩下的面积与原来的面积之比最大就选择哪个。

多次随机直到找到解,可以通过本题全部数据。

const int N = 2e5 + 7;
const ld eps = 1e-10L;
int n, k;
struct P {
	int l, d, r, u;
	inline P() {}
	inline P(int l, int d, int r, int u) : l(l), d(d), r(r), u(u) {}
	inline void in() { rd(l), rd(d), rd(r), rd(u); }
	inline ll s() {
		return (l > r || d > u) ? 0ll : 1ll * (r - l + 1) * (u - d + 1);
	}
} p[N];
vector<P> a;

inline ll calc(P a, P b) {
	a.l = max(a.l, b.l);
	a.d = max(a.d, b.d);
	a.r = min(a.r, b.r);
	a.u = min(a.u, b.u);
	return a.s();
}

inline void upd(P &a, P b) {
	a.l = max(a.l, b.l);
	a.d = max(a.d, b.d);
	a.r = min(a.r, b.r);
	a.u = min(a.u, b.u);
}

inline bool work() {
	a.clear();
	map<int, bool> v;
	for (int i = 0; i < k; i++) {
		int x = rdm(1, n);
		while (v[x]) x = rdm(1, n);
		v[x] = 1, a.pb(p[x]);
	}
	for (int i = 1; i <= n; i++)
		if (v.find(i) == v.end()) {
			ld x = 0.0L;
			int o = -1;
			for (int j = 0; j < k; j++) {
				ld y = 1.0L * calc(a[j], p[i]) / a[j].s();
				if (y > x + eps) x = y, o = j;
			}
			if (!~o) return 0;
			upd(a[o], p[i]);
		}
	return 1;
}

int main() {
	srand(time(0));
	rd(n), rd(k);
	for (int i = 1; i <= n; i++) p[i].in();
	while (!work());
	for (int i = 0; i < k; i++) print(a[i].l, ' '), print(a[i].d);
	return 0;
}

掃除 (Sweeping)

写一个可能过不了的做法。。。

先考虑没有操作 44,即没有加入新点的情况。

首先有一个显然的结论:对于至少被移动过一次的点 i,ji,j,一定不会出现 xi<xjx_i < x_j 且 yi<yjy_i < y_j 的情况。

因此,如果我们对至少被移动过一次的点,以横坐标从小到大为第一关键字,纵坐标从大到小为第二关键字排序,则每次移动的一定是一个区间,且移动前后排序关系不变。

考虑移动的本质为,将某一个区间里的点的横坐标或纵坐标对一个定值取 max⁡\max。

于是我们可以用一个支持区间修改,单点查询的数据结构维护,比如线段树。

那对于没有被移动过的点怎么办呢?

我们可以找到每个点第一次移动的时间,然后在那个时间插入到线段树中......

插入到线段树?这件事显然做不到,因此我们还要将线段树换成平衡树。

如何找到每个点第一次移动的时间呢?

由于移动涉及到的点都在一个矩形内,因此可以用可持久化线段树维护对于每个纵坐标,任意时刻受到影响的最大横坐标。然后对于每个点,在主席树上二分即可。

查询时,直接找到一个点在平衡树中的点,然后从这个点找到根再回溯,过程中下传区间修改时的标记即可。

设 m,qm,q 同阶,时间复杂度 O(mlog⁡2m)\mathcal O(m \log^2 m)。

如果有加入新点的操作,考虑二进制分组维护这个过程。

即,假设当前有 kk 个点,按照 kk 的二进制位下 11 的位置进行分组。比如如果 k=21k = 21,则分成 16+4+116+4+1 三组。显然,组的个数是 O(log⁡k)\mathcal O(\log k) 的。

对于每组内部维护没有加入新点时的情况。当加入一个新点时,分成的组会从后往前合并,合并的同时暴力重构这一组即可。

由于每个节点至多被重构 O(log⁡m)\mathcal O(\log m) 次,总时间复杂度为 O(mlog⁡3m)\mathcal O(m \log^3 m)。

代码不写了。。。

Day2

カメレオンの恋 (Chameleon's Love)

对于两个点 x,yx,y,如果我们按照如下方式建图:

  • 若 x,yx,y 颜色相同,则 x,yx,y 之间连一条无向边,记为 x↔yx \leftrightarrow y。
  • 若 xx 喜欢 yy,则连一条 x→yx \to y 的有向边。

先不考虑询问的限制次数。

如果我们询问所有的点对 (x,y)(x,y),返回的结果要么为 11,要么为 22。

如果为 11,则只有以下三种情况:

  1. x,yx,y 颜色相同。
  2. xx 喜欢 yy,但 yy 不喜欢 xx。
  3. yy 喜欢 xx,但 xx 不喜欢 yy。

容易发现,当答案为 11 时,x,yx,y 之间一定有边;答案为 22 时,x,yx,y 之间一定无边,除了 x→yx \to y 且 y→xy \to x 的情况。

进一步可以发现,每个点的度数都是 33,但是询问出来的度数可能为 11。

若询问出来 xx 的度数为 11,则说明存在一个度数同样为 11 的点 yy,符合 x→yx \to y 且 y→xy \to x 的情况,而询问出来的那条边,则是一条无向边 x↔zx \leftrightarrow z,这意味着 x,zx,z 的颜色相同。

若询问出来 xx 的度数为 33,设这 33 个点为 y,z,wy,z,w。考虑到如果我们询问 (x,y,z),(x,y,w),(x,z,w)(x,y,z),(x,y,w),(x,z,w),其中有且仅有一组的答案为 11,其它两组的答案都为 22。

不妨设为 11 的询问为 (x,y,z)(x,y,z),则只可能是 x↔yx \leftrightarrow y 且 z→xz \to x,或者 x↔zx \leftrightarrow z 且 y→xy \to x。

尽管我们无法得知是哪种,但是我们可以以此确定 x→wx \to w 这条边。

通过这种方法,我们可以确定所有的有向边,那么剩下的边就是无向边,即颜色相同的点。

这个做法的询问次数为 O(n2)\mathcal O(n^2) 的,瓶颈在询问两两点对上。

考虑优化这个过程。

我们可以通过 O(n)\mathcal O(n) 次询问求出一个极大独立集。

方法为,从 1∼2n1 \sim 2n 依次尝试加入当前的极大独立集 SS,如果询问 (S,x)(S,x) 的答案为 ∣S∣+1|S| + 1,则说明可以加入,否则说明有边。

找到极大独立集之后,对于独立集之外的点,在独立集中二分找到所有边。

此时还剩下独立集之外的点中的边没有找到,那么重复上述过程,直到所有剩下的点都是一个独立集。

由于每个点的度数最多为 33,因此每次独立集的大小 ≥\ge 总点数的 14\frac 14。这意味着每次我们可以将点的规模缩小到原来的 34\frac 34,那么一共只需要最多重复 O(log⁡n)\mathcal O(\log n) 次就能找到所有边,因此求最大独立集部分的询问次数为 O(nlog⁡n)\mathcal O(n \log n)。

又由于每找到一条边需要一次二分,而边数是 O(n)\mathcal O(n) 的,所以二分部分也只有 O(nlog⁡n)\mathcal O(n \log n) 次询问。

这样我们就可以用 O(nlog⁡n)\mathcal O(n \log n) 次询问找到所有边,为了减小常数防止被卡,可以先对点进行 random_shuffle。

inline int get(int x, vi p) {
	int l = 0, r = p.size() - 1;
	while (l < r) {
		int mid = (l + r) >> 1;
		vi q(1, x);
		for (int i = l; i <= mid; i++) q.pb(p[i]);
		if (Query(q) != (int)q.size()) r = mid;
		else l = mid + 1;
	}
	return p[l];
}

inline int ask(int x, int y, int z) {
	vi p(3);
	p[0] = x, p[1] = y, p[2] = z;
	return Query(p);
}

void Solve(int n) {
	srand(time(0));
	vi A, B;
	for (int i = 1; i <= (n << 1); i++) B.pb(i);
	random_shuffle(B.begin(), B.end());
	vector<vi> e(n << 1 | 1);
	while (B.size()) {
		vi S = B, v(n << 1 | 1);
		A.clear(), B.clear();
		for (int x : S) {
			A.pb(x);
			if (Query(A) != (int)A.size()) A.pop_back(), B.pb(x);
			else v[x] = 1;
		}
		for (int i : B) {
			vi p = A;
			p.pb(i);
			do {
				p.pop_back();
				int k = get(i, p);
				e[k].pb(i), e[i].pb(k);
				for (int &x : p)
					if (x == k) swap(x, p.back());
				p.pop_back();
				p.pb(i);
			} while (Query(p) != (int)p.size());
		}
	}
	vi v(n << 1 | 1);
	vector<map<int, bool>> f(n << 1 | 1);
	for (int x = 1; x <= (n << 1); x++)
		if (e[x].size() == 3u) {
			int y = e[x][0], z = e[x][1], w = e[x][2];
			if (ask(x, y, w) == 1) swap(z, w);
			else if (ask(x, z, w) == 1) swap(y, w);
			f[x][w] = f[w][x] = 1;
		}
	for (int x = 1; x <= (n << 1); x++)
		if (!v[x])
			for (int y : e[x])
				if (!f[x][y]) v[x] = v[y] = 1, Answer(x, y);
}

ジョイッターで友だちをつくろう (Making Friends on Joitter is Fun)

并查集维护每个极大团即可。

如果某一次加边后,存在两个团之间可以互相到达,则需要合并这两个团。

用 set 维护每个团内的节点,点团之间的边,和团团之间的边,合并时启发式合并。

时间复杂度 O(nlog⁡2n)\mathcal O(n \log^2 n)。

const int N = 1e5 + 7;
int n, m, f[N];
set<int> s[N], e[N], g[N], fe[N], fg[N];
deque<int> X, Y;
ll ans;

int get(int x) {
	return x == f[x] ? x : (f[x] = get(f[x]));
}

inline void work(int x, int y) {
	int fx = get(x), fy = get(y);
	if (fx == fy) return;
	if (fe[fy].find(fx) == fe[fy].end()) {
		if (g[fy].find(x) == g[fy].end())
			ans += s[fy].size(),
			e[x].insert(fy), g[fy].insert(x),
			fe[fx].insert(fy), fg[fy].insert(fx);
		return;
	}
	ans -= 1ll * s[fx].size() * (s[fx].size() - 1);
	ans -= 1ll * s[fy].size() * (s[fy].size() - 1);
	fe[fy].erase(fx), fg[fx].erase(fy);
	if (s[fx].size() < s[fy].size()) swap(fx, fy);
	for (int i : s[fy]) {
		for (int j : e[i])
			ans -= s[j].size(),
			g[j].erase(i),
			X.pb(i), Y.pb(j);
		e[i].clear();
	}
	for (int i : g[fy])
		ans -= s[fy].size(),
		e[i].erase(fy),
		X.pb(i), Y.pb(fx);
	g[fy].clear();
	for (int i : fe[fy]) fg[i].erase(fy);
	for (int i : fg[fy]) fe[i].erase(fy);
	for (int i : s[fy]) s[fx].insert(i);
	f[fy] = fx;
	ans += 1ll * s[fx].size() * (s[fx].size() - 1);
	ans += 1ll * g[fx].size() * s[fy].size();
	s[fy].clear();
}

int main() {
	rd(n), rd(m);
	for (int i = 1; i <= n; i++) f[i] = i, s[i].insert(i);
	for (int i = 1, x, y; i <= m; i++) {
		rd(x), rd(y), X.pb(x), Y.pb(y);
		while (X.size()) work(X[0], Y[0]), X.pop_front(), Y.pop_front();
		print(ans);
	}
	return 0;
}

最古の遺跡 3 (Ruins 3)

题目太神仙了,先咕咕咕着。

Day3

星座 3 (Constellation 3)

考虑从上往下选,则如果选择了一个星星,那下面就由一些星星不能选了。

建出笛卡尔树之后可以发现,笛卡尔树上面的每个节点都对应着一个极大矩形,而一棵星星所在的极大矩形,在笛卡尔树上正好是一条,从某个节点到其子树内某个叶子的路径。

因此对于一个笛卡尔树上的节点,其对应区间的上面最多只会有一颗星星。考虑树形 dp,设 fi,jf_{i,j} 表示在横坐标为 ii 的节点对应区间的上面,星星的纵坐标为 jj 时的最大权值和。

直接 dp 状态数有 O(n2)\mathcal O(n^2) 个,但有效的状态只和星星数有关。

因此用 map 记录有效状态,转移时启发式合并即可,时间复杂度 O(nlog⁡2n)\mathcal O(n \log^2 n)。

const int N = 2e5 + 7;
int n, m, a[N], s[N], t, l[N], r[N], rt;
map<int, ll> f[N];
ll g[N], z[N], ans, sum;

inline void work(int x, int y) {
	while (f[x].size() && f[x].begin() -> fi <= y)
		g[x] = max(g[x], f[x].begin() -> se + z[x]), f[x].erase(f[x].begin());
}

inline int merge(int x, int y) {
	if (f[x].size() < f[y].size()) swap(x, y);
	z[x] += g[y], z[y] += g[x], g[x] += g[y];
	for (auto o : f[y]) f[x][o.fi] = max(f[x][o.fi], o.se + z[y] - z[x]);
	return x;
}

int dfs(int x) {
	int y = x;
	if (l[x]) l[x] = dfs(l[x]), work(l[x], a[x]), y = merge(y, l[x]);
	if (r[x]) r[x] = dfs(r[x]), work(r[x], a[x]), y = merge(y, r[x]);
	return y;
}

int main() {
	rd(n);
	for (int i = 1; i <= n; i++) {
		rd(a[i]);
		int k = t;
		while (k && a[s[k]] < a[i]) --k;
		if (k) r[s[k]] = i;
		if (k < t) l[i] = s[k+1];
		s[++k] = i, t = k;
	}
	rt = s[1];
	rd(m);
	for (int i = 1, x, y, z; i <= m; i++)
		rd(x), rd(y), rd(z), f[x][y] = z, sum += z;
	rt = dfs(rt), ans = g[rt];
	for (auto o : f[rt]) ans = max(ans, o.se + z[rt]);
	print(sum - ans);
	return 0;
}

収穫 (Harvest)

题目太神仙了,先咕咕咕着。

迷い猫 (Stray Cat)

通信题,扔了。

Day4

首都 (Capital City)

答案相当于要找到一个包含颜色最少的连通块,满足连通块内的颜色和连通块外的颜色两两不同,我们称这样的连通块为合法连通块。

考虑点分治,对于每一次点分出来的重心和连通块,我们在连通块中找到最小的包含重心的合法连通块,用这个连通块的答案去更新答案。

这样,最优解一定会被更新到,时间复杂度 O(nlog⁡n)\mathcal O(n \log n)。

const int N = 2e5 + 7;
int n, m, a[N], c[N], ans, f[N], s[N], rt, mn;
vi e[N], b[N], p;
bool u[N], v[N], w[N];

void getrt(int x, int S) {
	v[x] = 1, s[x] = 1;
	if (!b[a[x]].size()) p.pb(a[x]);
	b[a[x]].pb(x); 
	int o = 0;
	for (int y : e[x])
		if (!v[y] && !w[y]) getrt(y, S), s[x] += s[y], o = max(o, s[y]);
	o = max(o, S - s[x]);
	if (o < mn) rt = x, mn = o;
	v[x] = 0;
}

void dfs(int x) {
	v[x] = 1, s[x] = 1;
	for (int y : e[x])
		if (!v[y] && !w[y]) f[y] = x, dfs(y), s[x] += s[y];
	v[x] = 0;
}

void solve(int x, int S) {
	rt = x, mn = S, f[x] = 0, getrt(x, S), dfs(rt);
	queue<int> q;
	q.push(a[rt]), u[a[rt]] = 1, v[rt] = 1;
	int cnt = 0;
	bool ok = (int)b[a[rt]].size() == c[a[rt]];
	while (ok && q.size()) {
		int x = q.front();
		q.pop();
		for (int y : b[x])
			while (!v[y]) {
				v[y] = 1;
				if (!u[a[y]]) {
					if ((int)b[a[y]].size() != c[a[y]]) ok = 0;
					q.push(a[y]), u[a[y]] = 1, ++cnt;
				}
				y = f[y];
			}
	}
	if (ok) ans = min(ans, cnt);
	for (int x : p) {
		for (int y : b[x]) v[y] = 0;
		b[x].clear(), u[x] = 0;
	}
	p.clear();
	w[rt] = 1;
	int now = rt;
	for (int nxt : e[now])
		if (!w[nxt]) solve(nxt, s[nxt]);
}

int main() {
	rd(n), rd(m), ans = m;
	for (int i = 1, x, y; i < n; i++)
		rd(x), rd(y), e[x].pb(y), e[y].pb(x);
	for (int i = 1; i <= n; i++) rd(a[i]), ++c[a[i]];
	solve(1, n), print(ans);
	return 0;
}

伝説の団子職人 (Legendary Dango Maker)(1,2,3,4,5,6)

提答题,扔了。

治療計画 (Treatment Project)

我们以横坐标为时间轴,纵坐标为位置轴构建平面直角坐标系。

考虑一个治疗方案 (Ti,Li,Ri)(T_i,L_i,R_i) 单独在这样一个坐标系中的覆盖区域,可以发现其覆盖的区域为以 (Ti,Li),(Ti,Ri)(T_i,L_i),(T_i,R_i) 为两个 45∘45^\circ 角向右的一个等腰直角三角形。

特别地,对于 Li=1L_i = 1 的方案,它还能多覆盖一点三角形下方的区域;对于 Ri=nR_i = n 的方案,它还能多覆盖一点三角形上方的区域。具体可以自己手画一下。

由于最终要求所有人都被治愈,因此一个方案合法当且仅当这些区域可以拼接起来且使某个 Li=1L_i=1 的方案与某个 Ri=nR_i = n 的方案连通。

注意,这里的拼接并不是指有交,对于方案 i,ji,j,ii 可以与 jj 单向地拼接当且仅当 ∣Ti−Tj∣≤Ri−Lj+1|T_i-T_j|\le R_i - L_j + 1,具体也可以自己手画一下。

显然,求解这个问题就是一个最短路。

直接连边边数是 O(m2)\mathcal O(m^2) 的,显然无法承受,用线段树 + set 优化连边即可。

这个优化连边的 Trick 跟 P5471 [NOI2019]弹跳 很像。具体来说,在点权最短路上,每个点最多只会被松弛一次,即对于每个点,第一次更新到它时的值就是它的最短路(在边权最短路中这显然是不一定的)。于是我们可以在某一个点被更新过就立刻删掉它,这样时间复杂度就与边数无关,为 O(nlog⁡n)\mathcal O(n \log n) 了。

这两道题都有一个共同点是,二维平面上从一个点到一个区域连边跑点权最短路,于是我们可以用一棵线段树维护第一维,线段树中每个节点用一个 set 存下在第一维里这个区间内的所有点,这样每个点被复制了 O(log⁡m)\mathcal O(\log m) 次,总点数为 O(mlog⁡m)\mathcal O(m \log m),跑上述所说的点权最短路,总时间复杂度为 O(mlog⁡2m)\mathcal O(m \log^2 m)。

const int N = 1e5 + 7;
const ll inf = 1e18;
int n, m;
struct P {
	int t, l, r, c;
	inline void in() { rd(t), rd(l), rd(r), rd(c); }
	inline bool operator < (const P o) const { return t < o.t; }
} p[N];
struct T {
	int l, r;
	set<pi> s;
} t1[N<<2], t2[N<<2];
ll d[N], ans = inf;
pq<pair<ll, int>> q;
bool v[N], w[N];

void build(T* t, int p, int l, int r) {
	t[p].l = l, t[p].r = r;
	if (l == r) return;
	build(t, ls, l, md), build(t, rs, md + 1, r);
}

void ins(T *t, int p, int x, pi k) {
	t[p].s.insert(k);
	if (t[p].l == t[p].r) return;
	if (x <= md) ins(t, ls, x, k);
	if (x > md) ins(t, rs, x, k);
}

void upd(T *t, int p, int l, int r, int x, ll k) {
	if (l <= t[p].l && r >= t[p].r) {
		while (t[p].s.size() && t[p].s.begin() -> fi <= x) {
			int y = t[p].s.begin() -> se;
			t[p].s.erase(t[p].s.begin());
			if (w[y]) continue;
			d[y] = k + ::p[y].c, q.push(mp(-d[y], y)), w[y] = 1;
		}
		return;
	}
	if (l <= md) upd(t, ls, l, r, x, k);
	if (r > md) upd(t, rs, l, r, x, k);
}

int main() {
	rd(m), rd(n);
	for (int i = 1; i <= n; i++) p[i].in();
	sort(p + 1, p + n + 1);
	build(t1, 1, 1, n), build(t2, 1, 1, n);
	for (int i = 1; i <= n; i++)
		ins(t1, 1, i, mp(p[i].l - p[i].t, i)),
		ins(t2, 1, i, mp(p[i].l + p[i].t, i));
	for (int i = 1; i <= n; i++)
		if (p[i].l != 1) d[i] = inf;
		else d[i] = p[i].c, q.push(mp(-d[i], i)), w[i] = 1;
	while (q.size()) {
		int x = q.top().se;
		q.pop();
		if (v[x]) continue;
		v[x] = 1;
		if (x != 1) upd(t1, 1, 1, x - 1, p[x].r - p[x].t + 1, d[x]);
		if (x != n) upd(t2, 1, x + 1, n, p[x].r + p[x].t + 1, d[x]);
	}
	for (int i = 1; i <= n; i++)
		if (p[i].r == m) ans = min(ans, d[i]);
	print(ans == inf ? -1 : ans);
	return 0;
}

标签:DP、二分、二分图、二进制分组、交互题、可持久化、启发式合并、平衡树、并查集、提答题、最短路、构造、树形 DP、点分治、笛卡尔树、线段树、计算几何、贪心、随机化

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…