【CodeForces】Codeforces Round 584

比赛链接

点击打开链接

官方题解

点击打开链接

Problem A. Paint the Numbers

注意到整除关系是具有传递性的,每次选择当前集合中最小的元素,删去其所有倍数即可。

时间复杂度 O ( N 2 ) O(N^2)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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("");
}
int a[MAXN];
int main() {
	int n; read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	sort(a + 1, a + n + 1);
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		bool found = false;
		for (int j = 1; j <= i - 1; j++)
			if (a[i] % a[j] == 0) found = true;
		if (!found) ans++;
	}
	writeln(ans);
	return 0;
}

Problem B. Koala and Lights

由于周期都较小,可以直接模拟若干轮该过程。

时间复杂度 O ( N × M a x { a i } ! ) O(N\times Max\{a_i\}!)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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("");
}
char s[MAXN];
int a[MAXN], b[MAXN];
int main() {
	int n; read(n);
	scanf("%s", s + 1);
	for (int i = 1; i <= n; i++)
		read(a[i]), read(b[i]);
	int ans = 0;
	for (int i = 1; i <= 10000; i++) {
		int now = 0;
		for (int j = 1; j <= n; j++)
			now += s[j] - '0';
		chkmax(ans, now);
		for (int j = 1; j <= n; j++)
			if (i >= b[j] && (i - b[j]) % a[j] == 0) {
				if (s[j] == '0') s[j] = '1';
				else s[j] = '0';
			}
	}
	writeln(ans);
	return 0;
}

Problem C. Paint the Digits

贪心地将较小的数字整体标上 1 ,直到无法整体标 1 后判断剩余部分是否有序。

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

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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("");
}
char s[MAXN];
int n, l[MAXN], r[MAXN];
void solve() {
	int now = 0;
	static char ans[MAXN];
	for (int i = 1; i <= n + 1; i++)
		ans[i] = 0;
	for (int p = 0; p <= 9; p++) {
		if (l[p] == -1) continue;
		if (l[p] < now) {
			char Max = 0;
			for (int i = 1; i <= n; i++)
				if (s[i] - '0' < p || (s[i] - '0' == p && i > now)) {
					ans[i] = '1';
				} else {
					ans[i] = '2';
					if (s[i] < Max) {
						puts("-");
						return;
					}
					Max = s[i];
				}
			printf("%s\n", ans + 1);
			return;
		} else now = r[p];
	}
	for (int i = 1; i <= n; i++)
		ans[i] = '1';
	printf("%s\n", ans + 1);
}
int main() {
	int T; read(T);
	while (T--) {
		read(n);
		scanf("\n%s", s + 1);
		for (int i = 0; i <= 9; i++)
			l[i] = r[i] = -1;
		for (int i = 1; i <= n; i++) {
			int x = s[i] - '0';
			if (l[x] == -1) l[x] = r[x] = i;
			else r[x] = i;
		}
		solve();
	}
	return 0;
}

Problem D. Cow and Snacks

将客人的偏好看做边,则每一个大小为 x x 的连通块能够产生的最大贡献应为 x 1 x-1

因此,用并查集维护各个连通块的大小即可。

时间复杂度 O ( N + K α ( N ) ) O(N+K\alpha(N))

扫描二维码关注公众号,回复: 10184596 查看本文章
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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("");
}
int n, k, f[MAXN];
int F(int x) {
	if (f[x] == x) return x;
	else return f[x] = F(f[x]);
}
int main() {
	read(n), read(k);
	for (int i = 1; i <= n; i++)
		f[i] = i;
	for (int i = 1; i <= k; i++) {
		int x, y; read(x), read(y);
		f[F(x)] = F(y);
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
		ans += F(i) != i;
	writeln(k - ans);
	return 0;
}

Problem E. Rotate Columns

注意到我们可以将所有列按照最大值排序,只取最大的 N N 列,可以将 M M 降至 O ( N ) O(N) 级别。

考虑状压 dp ,即依次考虑每一列,选取一些尚未填入的 r i r_i 填入所考虑的列上对应的值。则需要在状态中计入每个 r i r_i 是否已被选取。

转移时需要枚举一个尚未选取的 r i r_i 的一个子集 T T 进行选取,其权值仅与列号 i i T T 有关,可以在 O ( 2 N N 2 ) O(2^NN^2) 的时间内预处理。

单组数据时间复杂度 O ( 3 N N + 2 N N 2 ) O(3^NN+2^NN^2)

#include<bits/stdc++.h>
using namespace std;
const int MAXM = 15;
const int MAXN = 5005;
const int MAXS = 1 << 12;
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("");
}
int n, m, p[MAXN], Max[MAXN], a[MAXM][MAXN];
int dp[MAXM][MAXS], sum[MAXM][MAXS], add[MAXM][MAXS], bit[MAXM];
bool cmp(int x, int y) {
	return Max[x] > Max[y];
}
int main() {
	int T; read(T);
	while (T--) {
		read(n), read(m);
		for (int i = 1; i <= m; i++)
			Max[i] = 0, p[i] = i;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			read(a[i][j]), chkmax(Max[j], a[i][j]);
		sort(p + 1, p + m + 1, cmp);
		for (int i = 1; i <= n; i++)
			bit[i] = 1 << (i - 1);
		int goal = (1 << n) - 1;
		chkmin(m, n);
		memset(sum, 0, sizeof(sum));
		memset(add, 0, sizeof(add));
		for (int i = 1; i <= m; i++) {
			int pos = p[i];
			for (int s = 0; s <= goal; s++)
			for (int j = 1; j <= n; j++)
				if (bit[j] & s) sum[i][s] += a[j][pos];
			for (int s = 0; s <= goal; s++) {
				int t = s;
				for (int j = 1; j <= n; j++) {
					chkmax(add[i][s], sum[i][t]);
					if (t & 1) t += 1 << n;
					t >>= 1;
				}
			}
		}
		memset(dp, 0, sizeof(dp));
		for (int i = 0; i <= m - 1; i++)
		for (int s = 0; s <= goal; s++) {
			int tmp = dp[i][s];
			chkmax(dp[i + 1][s], tmp);
			for (int t = s; t != 0; t = (t - 1) & s)
				chkmax(dp[i + 1][s ^ t], tmp + add[i + 1][t]);
		}
		writeln(dp[m][0]);
	}
	return 0;
}

Problem F. Koala and Notebook

考虑比较高精度数的大小,我们应当优先比较位数,位数一样时比较字典序。

可以考虑将每一条边拆开,使得每一条边上至多只有一个字符。

由于首先要求位数最小,可以从 1 1 号点出发进行 0/1 BFS ,并保留最短路图。

此后,只要在最短路图上走,就可以保证位数最小,因此贪心地走较小的出边进行 DFS ,保证字典序最小即可。

时间复杂度 O ( M L o g M ) O(MLogM)​

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1.3e6 + 5;
const int P = 1e9 + 7;
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("");
}
int n, m, tot, ans[MAXN], dist[MAXN];
bool vis[MAXN]; int nxt[MAXN][11];
void addedge(int s, int t, int v) {
	vector <int> tmp; tmp.clear();
	while (v != 0) {
		tmp.push_back(v % 10);
		v /= 10;
	}
	while (tmp.size() > 0) {
		if (nxt[s][tmp.back()] == 0) nxt[s][tmp.back()] = ++tot;
		s = nxt[s][tmp.back()]; tmp.pop_back();
	}
	nxt[s][10] = t;
}
void dfs(vector <int> pos) {
	for (auto x : pos) {
		if (nxt[x][10] && !vis[nxt[x][10]] && dist[nxt[x][10]] == dist[x]) {
			ans[nxt[x][10]] = ans[x];
			vis[nxt[x][10]] = true;
			pos.push_back(nxt[x][10]);
		}
	}
	for (int i = 0; i <= 9; i++) {
		vector <int> dest;
		for (auto x : pos) {
			if (nxt[x][i] && !vis[nxt[x][i]] && dist[nxt[x][i]] == dist[x] + 1) {
				ans[nxt[x][i]] = (10ll * ans[x] + i) % P;
				vis[nxt[x][i]] = true;
				dest.push_back(nxt[x][i]);
			}
		}
		if (!dest.empty()) dfs(dest);
	}
}
int main() {
	read(n), read(m), tot = n;
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		addedge(x, y, i);
		addedge(y, x, i);
	}
	static int q[2 * MAXN]; 
	int l = MAXN, r = MAXN; q[MAXN] = 1, dist[1] = 1;
	while (l <= r) {
		int pos = q[l++];
		if (nxt[pos][10] && dist[nxt[pos][10]] == 0) {
			dist[nxt[pos][10]] = dist[pos];
			q[--l] = nxt[pos][10];
		}
		for (int i = 0; i <= 9; i++)
			if (nxt[pos][i] && dist[nxt[pos][i]] == 0) {
				dist[nxt[pos][i]] = dist[pos] + 1;
				q[++r] = nxt[pos][i];
			}
	}
	vector <int> tmp;
	tmp.push_back(1);
	vis[1] = true, dfs(tmp);
	for (int i = 2; i <= n; i++)
		writeln(ans[i]);
	return 0;
}

Problem G. Into Blocks

考虑问题的简单版本,应当将序列分为尽可能多的段,使得每一段都包含了所包含的元素的所有出现位置,然后将每一段改为出现次数最多的元素。

考虑如何维护上述过程,首先我们需要用更加适合维护的语言重新描述该过程。

令元素 i i 的第一、最后一次出现的位置为 l i , r i l_i,r_i ,那么若将 [ l i , r i ) [l_i,r_i) 内所有位置 + 1 +1 ,那么最终值为 0 0 的位置后即为需要分开的断点。得出断点后,求出相邻两个断点之间出现次数最多的元素的出现次数之和 S S ,则答案为 N S N-S

那么,我们可以用 set 维护每一种元素的出现位置,并用线段树维护区间出现次数最多的元素的出现次数。再用另一棵支持区间加减的线段树维护断点,在节点上计入区间最小值和区间最小值之间出现次数最多的元素的出现次数之和,这样的信息是可以借助上面的线段树合并的。

时间复杂度 O ( N L o g N + Q L o g 2 N ) O(NLogN+QLog^2N)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
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 SegmentTreeMax {
	struct Node {
		int lc, rc;
		int Max;
	} a[MAXN * 2];
	int n, root, size;
	void build(int &root, int l, int r) {
		root = ++size;
		a[root].Max = 0;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	void update(int root) {
		a[root].Max = max(a[a[root].lc].Max, a[a[root].rc].Max);
	}
	void modify(int root, int l, int r, int pos, int val) {
		if (l == r) {
			a[root].Max = val;
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) modify(a[root].lc, l, mid, pos, val);
		else modify(a[root].rc, mid + 1, r, pos, val);
		update(root);
	}
	void modify(int pos, int val) {
		modify(root, 1, n, pos, val);
	}
	int query(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].Max;
		int mid = (l + r) / 2, ans = 0;
		if (mid >= ql) chkmax(ans, query(a[root].lc, l, mid, ql, min(mid, qr)));
		if (mid + 1 <= qr) chkmax(ans, query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr));
		return ans;
	}
	int query(int l, int r) {
		return query(root, 1, n, l, r);
	}
} STA;
struct info {int Min, l, r, sum; };
info operator + (info a, info b) {
	if (a.Min < b.Min) return a;
	if (b.Min < a.Min) return b;
	info ans; ans.Min = a.Min;
	ans.l = a.l, ans.r = b.r;
	ans.sum = a.sum + b.sum + STA.query(a.r + 1, b.l);
	return ans;
}
struct SegmentTreeMin {
	struct Node {
		int lc, rc, tag;
		info ans;
	} a[MAXN * 2];
	int n, root, size;
	void update(int root) {
		a[root].ans = a[a[root].lc].ans + a[a[root].rc].ans;
	}
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) {
			a[root].ans = (info) {0, l, l, 0};
			return;
		}
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
		update(root);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 0, n);
	}
	void pushdown(int root) {
		if (a[root].tag) {
			a[a[root].lc].tag += a[root].tag;
			a[a[root].lc].ans.Min += a[root].tag;
			a[a[root].rc].tag += a[root].tag;
			a[a[root].rc].ans.Min += a[root].tag;
			a[root].tag = 0;
		}
	}
	void modify(int root, int l, int r, int pos, int val) {
		if (l == r) {
			STA.modify(pos, val);
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= pos) modify(a[root].lc, l, mid, pos, val);
		else modify(a[root].rc, mid + 1, r, pos, val);
		update(root);
	}
	void modify(int pos, int val) {
		modify(root, 0, n, pos, val);
	}
	void modify(int root, int l, int r, int ql, int qr, int d) {
		if (l == ql && r == qr) {
			a[root].tag += d;
			a[root].ans.Min += d;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) modify(a[root].lc, l, mid, ql, min(mid, qr), d);
		if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);
		update(root);
	}
	void modify(int l, int r, int d) {
		modify(root, 0, n, l, r, d);
	}
} STI;
int n, q, Max, a[MAXN];
set <int> pos[MAXN];
int main() {
	read(n), read(q);
	for (int i = 1; i <= n; i++) {
		read(a[i]), chkmax(Max, a[i]);
		pos[a[i]].insert(i);
	}
	STA.init(n);
	STI.init(n);
	for (int i = 1; i <= Max; i++)
		if (pos[i].size()) {
			STI.modify(*pos[i].begin(), pos[i].size());
			if (pos[i].size() >= 2) {
				auto tmp = pos[i].end(); tmp--;
				STI.modify(*pos[i].begin(), *tmp - 1, 1);
			}
		}
	writeln(n - STI.a[STI.root].ans.sum);
	while (q--) {
		int x, v;
		read(x), read(v);
		STI.modify(*pos[a[x]].begin(), 0);
		if (pos[a[x]].size() >= 2) {
			auto tmp = pos[a[x]].end(); tmp--;
			STI.modify(*pos[a[x]].begin(), *tmp - 1, -1);
		}
		pos[a[x]].erase(x);
		if (pos[a[x]].size()) {
			STI.modify(*pos[a[x]].begin(), pos[a[x]].size());
			if (pos[a[x]].size() >= 2) {
				auto tmp = pos[a[x]].end(); tmp--;
				STI.modify(*pos[a[x]].begin(), *tmp - 1, 1);
			}
		}
		a[x] = v;
		if (pos[a[x]].size()) {
			STI.modify(*pos[a[x]].begin(), 0);
			if (pos[a[x]].size() >= 2) {
				auto tmp = pos[a[x]].end(); tmp--;
				STI.modify(*pos[a[x]].begin(), *tmp - 1, -1);
			}
		}
		pos[a[x]].insert(x);
		STI.modify(*pos[a[x]].begin(), pos[a[x]].size());
		if (pos[a[x]].size() >= 2) {
			auto tmp = pos[a[x]].end(); tmp--;
			STI.modify(*pos[a[x]].begin(), *tmp - 1, 1);
		}
		writeln(n - STI.a[STI.root].ans.sum);
	}
	return 0;
}

Problem H. Moving Walkways

首先,我们可以将每一段路都看作走道,并将所有走道的速度 + 1 +1 ,步行速度 1 -1

这样做的好处是使得能量增减的速度等于步行速度,此时,可以发现,能量即为步行速度对时间的积分,从而可以将能量看做路程。

对于一段长度为 L L ,速度为 v v 的走道,若投入 x x 点能量,则耗时为 L x v \frac{L-x}{v} 。同时,由于步行速度存在 [ 1 , 1 ] [-1,1] 的限制,投入的能量总数同样存在限制 l i 0 , r i 0 l_i\leq0,r_i\geq0 ,需要满足 l i x r i l_i\leq x\leq r_i

从上面耗时计算方式可以看出,能量应当尽可能地用在速度较小的走道上。

因此,可以首先默认以最节省能量的方式通过所有走道,计算耗时,然后从慢到快考虑每一条走道,尽可能地将能量用在这条走道上。为此,我们需要一棵支持区间加减和求区间最小值的线段树。

同时,注意到速度为 1 1 的走道实际上可以节省无限的能量,但是这么做是没有意义的,因为显然我们会在后来再将能量用在这条走道上,因此我们可以认为在速度为 1 1 的走道上不需要节省能量。

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

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5 + 5;
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 SegmentTree {
	struct Node {
		int lc, rc;
		double tag, Min;
	} a[MAXN * 2];
	int n, root, size;
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	void update(int root) {
		a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min);
	}
	void pushdown(int root) {
		if (a[root].tag != 0) {
			a[a[root].lc].tag += a[root].tag;
			a[a[root].lc].Min += a[root].tag;
			a[a[root].rc].tag += a[root].tag;
			a[a[root].rc].Min += a[root].tag;
			a[root].tag = 0;
		}
	}
	void modify(int root, int l, int r, int ql, int qr, double d) {
		if (l == ql && r == qr) {
			a[root].tag += d;
			a[root].Min += d;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) modify(a[root].lc, l, mid, ql, min(mid, qr), d);
		if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);
		update(root);
	}
	void modify(int l, int r, double d) {
		modify(root, 1, n, l, r, d);
	}
	double query(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].Min;
		pushdown(root);
		int mid = (l + r) / 2; double ans = 1e18;
		if (mid >= ql) chkmin(ans, query(a[root].lc, l, mid, ql, min(mid, qr)));
		if (mid + 1 <= qr) chkmin(ans, query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr));
		return ans;
	}
	double query(int l, int r) {
		return query(root, 1, n, l, r);
	}
} ST;
int n, m, p[MAXN];
double l[MAXN], r[MAXN];
double d[MAXN], v[MAXN], L;
int main() {
	read(m), read(L);
	double last = 0;
	for (int i = 1; i <= m; i++) {
		double l, r, x;
		scanf("%lf%lf%lf", &l, &r, &x);
		if (l - last >= 1e-8) {
			d[++n] = l - last;
			v[n] = 1;
		}
		d[++n] = r - l;
		v[n] = x + 1;
		last = r;
	}
	if (L - last >= 1e-8) {
		d[++n] = L - last;
		v[n] = 1;
	}
	ST.init(n);
	double ans = 0;
	for (int i = 1; i <= n; i++) {
		p[i] = i;
		if (v[i] == 1) {
			double tl = d[i] / (v[i] + 1), tr = d[i] / v[i];
			l[i] = d[i] - tr * v[i], r[i] = d[i] - tl * v[i], ans += tr;
		} else {
			double tl = d[i] / (v[i] + 1), tr = d[i] / (v[i] - 1);
			l[i] = d[i] - tr * v[i], r[i] = d[i] - tl * v[i], ans += tr;
		}
		ST.modify(i, n, -l[i]);
	}
	sort(p + 1, p + n + 1, [&] (int x, int y) {return v[x] < v[y]; } );
	for (int i = 1; i <= n; i++) {
		int pos = p[i];
		double delta = min(r[pos] - l[pos], ST.query(pos, n));
		ST.modify(pos, n, -delta);
		ans -= delta / v[pos];
	}
	printf("%.10lf\n", ans);
	return 0;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

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