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

这篇博客详细解析了IOI 2020集训队作业中的多个Codeforces竞赛题目,包括Orchestra、Bear and Paradox、Bear and Chemistry等,涉及动态规划、图论和数据结构等算法,介绍了如何在不同场景下优化算法以达到高效解决方案,如利用平衡树、链表、后缀树等数据结构。
摘要由CSDN通过智能技术生成

Codeforces 634F Orchestra

若枚举矩形的上下边界,则问题显然可以 O ( n ) O(n) O(n) 解决。

那么,考虑枚举矩形的上边界,再从上到下枚举矩形的下边界,则我们需要一边插入点,一边动态维护上面问题的答案。若直接实现,需要使用平衡树。

事实上,我们可以从下到上枚举矩形的下边界,则只需要链表就可以维护了。

时间复杂度 O ( r 2 + r n k ) O(r^2+rnk) O(r2+rnk)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3005;
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 r, c, n, k, home[MAXN];
int x[MAXN], y[MAXN], pre[MAXN], nxt[MAXN];
vector <int> a[MAXN];
bool cmp(int a, int b) {
	return y[a] < y[b];
}
int main() {
	read(r), read(c), read(n), read(k);
	for (int i = 1; i <= n; i++) {
		read(x[i]), read(y[i]), home[i] = i;
		a[x[i]].push_back(i);
	}
	y[n + 1] = 0, y[n + 2] = c + 1;
	sort(home + 1, home + n + 1, cmp);
	home[0] = n + 1, home[n + 1] = n + 2;
	ll ans = r * (r + 1ll) * c * (c + 1ll) / 4;
	for (int p = 1; p <= r; p++) {
		for (int i = 1; i <= n + 1; i++) {
			pre[home[i]] = home[i - 1];
			nxt[home[i - 1]] = home[i];
		}
		ll sum = 0;
		for (int i = n + 1; i != n + 2; i = nxt[i]) {
			int len = y[nxt[i]] - y[i];
			sum += (len - 1ll) * len / 2;
			for (int j = nxt[i], cnt = 1; j != n + 2 && cnt <= k - 1; j = nxt[j], cnt++)
				sum += len * (y[nxt[j]] - y[j]);
		}
		pre[n + 1] = n + 1, nxt[n + 2] = n + 2;
		for (int i = 1; i <= p - 1; i++) {
			for (auto x : a[i]) {
				int pos = x;
				for (int j = 1; j <= k; j++)
					pos = pre[pos];
				int qos = pos;
				for (int j = 1; j <= k; j++)
					qos = nxt[qos];
				while (pos != x && qos != n + 2) {
					sum += (y[nxt[pos]] - y[pos]) * (y[nxt[qos]] - y[qos]);
					pos = nxt[pos];
					qos = nxt[qos];
				}
				int p = pre[x], s = nxt[x];
				nxt[p] = s, pre[s] = p;
			}
		}
		for (int i = r; i >= p; i--) {
			ans -= sum;
			for (auto x : a[i]) {
				int pos = x;
				for (int j = 1; j <= k; j++)
					pos = pre[pos];
				int qos = pos;
				for (int j = 1; j <= k; j++)
					qos = nxt[qos];
				while (pos != x && qos != n + 2) {
					sum += (y[nxt[pos]] - y[pos]) * (y[nxt[qos]] - y[qos]);
					pos = nxt[pos];
					qos = nxt[qos];
				}
				int p = pre[x], s = nxt[x];
				nxt[p] = s, pre[s] = p;
			}
		}
	}
	cout << ans << endl;
	return 0;
}

Codeforces 639E Bear and Paradox

考虑确定最优策略,由调整法,可以证明, i i i 应当排在 j j j 之前当且仅当 p j t i < p i t j p_jt_i<p_it_j pjti<pitj ;而对于 p j t i = p i t j p_jt_i=p_it_j pjti=pitj i , j i,j i,j 的顺序是任意的。

那么,二分答案,考虑判断答案 c c c 是否合法。

则要求对于分数 x , y   ( x < y ) x,y\ (x<y) x,y (x<y) ,分数为 x x x 的题目可能的最大分数不超过分数为 y y y 的题目的最小分数,不难发现只要检查相邻的分数之间的偏序关系就可以了。

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

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const double eps = 1e-10;
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;
}
double sumt, pre[MAXN]; int Min[MAXN], Max[MAXN];
int n, a[MAXN], b[MAXN], p[MAXN], t[MAXN];
bool cmp(int x, int y) {
	return p[x] < p[y];
}
bool cnp(int x, int y) {
	return 1ll * t[x] * p[y] < 1ll * t[y] * p[x];
}
bool check(double c) {
	double last;
	for (int i = 1, nxt; i <= n; i = nxt + 1) {
		nxt = i; while (nxt + 1 <= n && p[b[i]] == p[b[nxt + 1]]) nxt++;
		if (i != 1) {
			for (int j = i; j <= nxt; j++) {
				double cur = p[b[j]] * (1 - c * pre[Max[b[j]]] / sumt);
				if (cur < last) return false;
			}
		}
		last = 0;
		for (int j = i; j <= nxt; j++)
			chkmax(last, p[b[j]] * (1 - c * (pre[Min[b[j]] - 1] + t[b[j]]) / sumt));
	}
	return true;
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		a[i] = b[i] = i;
	for (int i = 1; i <= n; i++)
		read(p[i]);
	for (int i = 1; i <= n; i++)
		read(t[i]), sumt += t[i];
	sort(a + 1, a + n + 1, cnp);
	sort(b + 1, b + n + 1, cmp);
	for (int i = 1; i <= n; i++)
		pre[i] = pre[i - 1] + t[a[i]];
	for (int i = 1, nxt; i <= n; i = nxt + 1) {
		nxt = i; while (nxt + 1 <= n && 1ll * p[a[i]] * t[a[nxt + 1]] == 1ll * t[a[i]] * p[a[nxt + 1]]) nxt++;
		for (int j = i; j <= nxt; j++) {
			Min[a[j]] = i;
			Max[a[j]] = nxt;
		}
	}
	double l = 0, r = 1;
	while (l + eps < r) {
		double mid = (l + r) / 2;
		if (check(mid)) l = mid;
		else r = mid;
	}
	printf("%.10lf\n", (l + r) / 2);
	return 0;
}

Codeforces 639F Bear and Chemistry

首先,题目的要求等价于给定的点集在同一边双连通分量内。

将给定的原图缩点,则剩余部分是一片森林。

对于每个询问,将涉及到的点取出,建立虚树,再在虚树上运行 Tarjan 计算答案即可。

时间复杂度 O ( N + M + ∑ ( n i + m i ) L o g N ) O(N+M+\sum(n_i+m_i)LogN) O(N+M+(ni+mi)LogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 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 <int> a[MAXN], b[MAXN], c[MAXN], d[MAXN];
int n, m, q, f[MAXN], x[MAXN], y[MAXN];
int father[MAXN][MAXLOG], depth[MAXN];
int timer, dfn[MAXN], cnt, num[MAXN];

bool instack[MAXN]; ll online;
int Timer, Dfn[MAXN], Low[MAXN];
int top, Stack[MAXN], tot, belong[MAXN];
void tarjan(int pos, int fa) {
	Low[pos] = Dfn[pos] = ++Timer;
	Stack[++top] = pos;
	instack[pos] = true;
	for (unsigned i = 0; i < c[pos].size(); i++) {
		if (d[pos][i] == fa) continue;
		if (Dfn[c[pos][i]] == 0) {
			tarjan(c[pos][i], d[pos][i]);
			Low[pos] = min(Low[pos], Low[c[pos][i]]);
		} else if (instack[c[pos][i]]) Low[pos] = min(Low[pos], Dfn[c[pos][i]]);
	}
	if (Low[pos] == Dfn[pos]) {
		int tmp = Stack[top--];
		belong[tmp] = ++tot;
		instack[tmp] = false;
		while (tmp != pos) {
			tmp = Stack[top--];
			belong[tmp] = tot;
			instack[tmp] = false;
		}
	}
}

int find(int x) {
	if (f[x] == x) return x;
	else return f[x] = find(f[x]);
}
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];
}
void addedge(int x, int y) {
	static int ind = 0; ind++;
	c[x].push_back(y);
	d[x].push_back(ind);
	c[y].push_back(x);
	d[y].push_back(ind);
}
inline int func(int x) {
	x = (x + online) % n;
	if (x == 0) x = n;
	return x;
}
void solve(int task) {
	static int vis[MAXN], points[MAXN];
	static int pos[MAXN], x[MAXN], y[MAXN];
	int n, m, cnt = 0; read(n), read(m);
	auto mark = [&] (int x) {
		if (vis[x] != task) {
			vis[x] = task;
			points[++cnt] = x;
		}
		if (vis[num[x]] != task) {
			vis[num[x]] = task;
			points[++cnt] = num[x];
		}
	};
	for (int i = 1; i <= n; i++) {
		read(pos[i]), pos[i] = func(pos[i]);
		mark(pos[i] = find(pos[i]));
	}
	for (int i = 1; i <= m; i++) {
		read(x[i]), read(y[i]);
		x[i] = func(x[i]);
		y[i] = func(y[i]);
		mark(x[i] = find(x[i]));
		mark(y[i] = find(y[i]));
	}
	sort(points + 1, points + cnt + 1, [&] (int x, int y) {return dfn[x] < dfn[y];});
	int top = 0, cmt = cnt; static int Stack[MAXN];
	for (int i = 1; i <= cnt; i++) {
		int x = points[i];
		if (num[points[i - 1]] != num[points[i]]) {
			while (top >= 2) {
				addedge(Stack[top], Stack[top - 1]);
				top--;
			}
			Stack[top = 1] = x;
		} else {
			while (top >= 2 && depth[lca(x, Stack[top])] <= depth[Stack[top - 1]]) {
				addedge(Stack[top], Stack[top - 1]);
				top--;
			}
			if (lca(x, Stack[top]) == Stack[top]) Stack[++top] = x;
			else {
				int y = lca(x, Stack[top]);
				points[++cmt] = y;
				addedge(Stack[top], y);
				Stack[top] = y;
				Stack[++top] = x;
			}
		}
	}
	while (top >= 2) {
		addedge(Stack[top], Stack[top - 1]);
		top--;
	}
	for (int i = 1; i <= m; i++)
		addedge(x[i], y[i]);
	for (int i = 1; i <= cnt; i++)
		if (Dfn[points[i]] == 0) tarjan(points[i], 0);
	bool ans = true;
	for (int i = 1; i <= n; i++)
		ans &= belong[pos[i]] == belong[pos[1]];
	if (ans) puts("YES"), online += task;
	else puts("NO");
	
	tot = Timer = 0;
	for (int i = 1; i <= cmt; i++) {
		c[points[i]].clear();
		d[points[i]].clear();
		Dfn[points[i]] = 0;
	}
}
void dfs(int pos, int fa) {
	father[pos][0] = fa;
	depth[pos] = depth[fa] + 1;
	for (auto x : a[pos])
		if (x != fa) dfs(x, pos);
}
void work(int pos, int fa, int cur) {
	depth[pos] = depth[fa] + 1;
	father[pos][0] = fa;
	dfn[pos] = ++timer, num[pos] = cur;
	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) work(b[pos][i], pos, cur);
}
int main() {
	read(n), read(m), read(q);
	for (int i = 1; i <= n; i++)
		f[i] = i;
	static bool non[MAXN];
	for (int i = 1; i <= m; i++) {
		read(x[i]), read(y[i]);
		int tx = find(x[i]);
		int ty = find(y[i]);
		if (tx == ty) non[i] = true;
		else {
			f[tx] = ty;
			a[x[i]].push_back(y[i]);
			a[y[i]].push_back(x[i]);
		}
	}
	for (int i = 1; i <= n; i++)
		f[i] = i;
	for (int i = 1; i <= n; i++)
		if (father[i][0] == 0) dfs(i, 0);
	for (int i = 1; i <= m; i++)
		if (non[i]) {
			int tx = find(x[i]), ty = find(y[i]);
			while (tx != ty) {
				if (depth[tx] < depth[ty]) swap(tx, ty);
				f[tx] = find(father[tx][0]), tx = find(tx);
			}
		}
	for (int i = 1; i <= n; i++)
	for (auto x : a[i]) if (find(i) != find(x)) b[find(i)].push_back(find(x));
	for (int i = 1; i <= n; i++)
		if (find(i) == i && dfn[i] == 0) {
			work(i, 0, i);
		}
	for (int i = 1; i <= q; i++)
		solve(i);
	return 0;
}

Codeforces 666D Chain Reaction

首先枚举各个机器人与正方形的四个角落的对应关系。

若四个机器人移动的横竖方向一致,则我们可以确定正方形的边长 L L L ,问题即为给定 a 1 , a 2 , a 3 , a 4 a_1,a_2,a_3,a_4 a1,a2,a3,a4 ,选取 x x x ,最小化 m a x { ∣ a i + [ i ≤ 2 ] L − x ∣ } max\{|a_i+[i\leq 2]L-x|\} max{ ai+[i2]Lx}
a 1 = a 1 + L , a 2 = a 2 + L a_1=a_1+L,a_2=a_2+L a1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值