Loading

盖世计划--0806--B班训练

A

我的题解

B

神秘题

根据 2008 年集训队论文可以给出 \(O(n)\) 做法,不会。

考虑从 \(k\) 小的情况出发。

\(k=1\) 时,结论:当且仅当 \(n\)\(2\)幂次时,先手必败。可以通过二进制构造方案求解。

\(k=2\) 时,结论:当且仅当 \(n\)斐波那契数时先手必败。将每个数通过斐波那契数分解,用 \(k=1\) 的方法证明。

否则,我们希望能够将 \(n\) 通过某个神秘序列分解,使得用 \(k=1\) 的方法先手能够必胜。

这个神秘序列满足:

  1. 可以将 \(1-n\) 之内的数分解为若干序列中的数,使得排序后相邻数至少为 \(k\) 倍关系。

然后大力构造。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*

*/
const int N = 2e7 + 10;
int a[N], b[N];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	int n, k;
	std::cin >> n >> k;

	a[0] = b[0] = 1;
	int i = 0, j = 0;
	while(a[i] < n) {
		i++;
		a[i] = b[i - 1] + 1;
		while(a[j] * k < a[i]) j++;
		j--;
		if(a[j] * k < a[i]) b[i] = b[j] + a[i];
		else b[i] = a[i];
	}

	if(a[i] == n) {
		std::cout << "lose\n";
		return 0;
	}

	i--;
	while(n) {
		if(n == a[i]) break;
		if(n > a[i]) n -= a[i];
		i--;
	}

	std::cout << a[i] << "\n";

	return 0;
}

C

博弈论

每个点都有必胜或必败状态,可以线性求出。

如果原本 \(\texttt{Alice}\) 就能赢,那么代价为 \(0\)

否则加边。首先加边不能构成环,不然会平局。

然后起点在加边后能够到达,终点的状态一定是先手必败。

总结一下,合法的加边满足:

  1. 不连向祖先
  2. 起点在加边后能够到达
  3. 终点的状态一定是先手必败

2 情况的解决方法是模拟博弈的过程。假如已知起点,那么终点就是不包含到根路径的,所有状态为必败的,节点的权值最小值。两遍 bfs 解决,一次从左到右,一次从右到左,回溯时更新最小值。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*

*/
const int N = 1e6 + 10;
i64 T, n, t, A, B, d, ans, mn;
i64 a[N];
i64 f[N], g[N];
std::vector<int> e[N];
i64 mnl[N], mnr[N];
i64 read() {
	i64 x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = (x << 1) + (x << 3) + (c - '0');
		c = getchar();
	}
	return x * f;
}
i64 dfs1(int u) {
	f[u] = g[u] = 0;
	mnl[u] = linf;
	for(int v : e[u]) {
		mn = std::min(mn, dfs1(v));
		f[u] |= !f[v];
		g[u] += !f[v];
	}
	mnl[u] = mn;
	return std::min(mn, !f[u] ? a[u] : linf);
}
i64 dfs2(int u) {
	mnr[u] = linf;
	for(int i = e[u].size() - 1; i >= 0; i--) {
		int v = e[u][i];
		mn = std::min(mn, dfs2(v));
	}
	mnr[u] = mn;
	return std::min(mn, !f[u] ? a[u] : linf);
}
void imit(int u, int now) {
	if(!now) {
		if(std::min(mnl[u], mnr[u]) != linf) ans = std::min(ans, A * a[u] + B * std::min(mnl[u], mnr[u]));
	}
	for(int v : e[u]) {
		if((g[u] == 1 && !f[v]) || !now) {
			imit(v, now ^ 1);
		}
	}
} 
void fake_main() {
	d = 0, ans = linf;
	n = read(), t = read(), A = read(), B = read();
	for(int i = 2; i <= n; i++) {
		int fa = read();
		e[fa].pb(i);
	}
	for(int i = 1; i <= n; i++) a[i] = read();
	mn = linf, dfs1(1);
	mn = linf, dfs2(1); 

	if((!t && f[1]) || (t && !f[1])) {
		printf("0\n");
		for(int i = 1; i <= n; i++) e[i].clear();
		return;
	} 
	imit(1, t);

	printf("%lld\n", (ans != linf) ? ans : -1);
	for(int i = 1; i <= n; i++) e[i].clear();
}	
int main() {

	// T = read(); 
	T = 1;
	while(T--) fake_main();

	return 0;
}

D

计数题,首先不考虑第二个限制,可以设 \(g_{i,j}\) 求出 \(i\) 个球,\(j\) 种颜色的方案数。

然后设 \(f_{i,j}\) 表示前 \(i\) 层,第 \(i\)\(j\) 种颜色的方案数。

预处理排列数和阶乘就做完了。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*

*/
const int N = 1e6 + 10, M = 5e3 + 10;
i64 n, m, p, maxn, sum;
i64 l[N];
i64 A[M], fac[M], g[M][M], f[2][M];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> m >> p;

	for (int i = 1; i <= n; i++) std::cin >> l[i], maxn = std::max(maxn, l[i]);

	A[0] = 1;
	for(int i = 1; i <= maxn; i++) A[i] = A[i - 1] * (m - i + 1) % p;
	fac[0] = 1;
	for(int i = 1; i <= maxn; i++) fac[i] = fac[i - 1] * i % p;

	g[0][0] = 1;
	for(int i = 1; i <= maxn; i++) {
		for(i64 j = 1; j <= maxn; j++) {
			g[i][j] = (g[i - 1][j] * (j - 1) % p + g[i - 1][j - 1]) % p;
		} 
	}

	int lst = 1, cur = 0;
	sum = 1;
	for(int i = 1; i <= n; i++) {
		std::swap(lst, cur);
		for(int j = 1; j <= std::min(m, l[i]); j++) {
			f[cur][j] = (A[j] * g[l[i]][j] % p * sum % p - fac[j] * g[l[i]][j] % p * f[lst][j] % p + p) % p;
		}
		sum = 0;
		for(int j = 1; j <= std::min(m, l[i]); j++) sum = (sum + f[cur][j]) % p;
		for(int j = 1; j <= l[i - 1]; j++) f[lst][j] = 0;
	}

	std::cout << sum << "\n";

	return 0;
}

E

不合法的数只有一种,考虑贪心地,把不合法的数尽可能多的并到合法的数中,假设不合法的数为 \(s\),总数为 \(n\),那么最多能并 \(n-s+1\) 个,剩下的不合法的分为一个一个。

考虑到摩尔投票法,用它和线段树维护区间绝对众数。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*

*/
const int N = 1e6 + 10;
int n, q;
struct seg {
	int v, c;
} t[N << 2];
std::vector<int> v[N];
int a[N];
void pushup(seg &u, seg ls, seg rs) {
	if(ls.v == rs.v) {
		u.v = ls.v, u.c = ls.c + rs.c;
	} else if(ls.c < rs.c) {
		u.v = rs.v, u.c = rs.c - ls.c;
	} else {
		u.v = ls.v, u.c = ls.c - rs.c;
	}
}
void build(int u, int l, int r) {
	if(l == r) {
		t[u] = {a[l], 1};
		return;
	}
	int mid = (l + r) >> 1;
	build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
	pushup(t[u], t[u << 1], t[u << 1 | 1]);
}
seg qry(int u, int l, int r, int L, int R) {
	if(L <= l && r <= R) return t[u];
	int mid = (l + r) >> 1;
	if(R <= mid) return qry(u << 1, l, mid, L, R);
	if(L > mid) return qry(u << 1 | 1, mid + 1, r, L, R);
	seg ret = {0, 0}, ls = qry(u << 1, l, mid, L, R), rs = qry(u << 1 | 1, mid + 1, r, L, R);
	pushup(ret, ls, rs);
	return ret;
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> q;

	for(int i = 1; i <= n; i++) {
		std::cin >> a[i];
		v[a[i]].pb(i);
	}
	build(1, 1, n);

	while(q--) {
		int l, r;
		std::cin >> l >> r;
		int x = qry(1, 1, n, l, r).v;
		int sum = std::upper_bound(v[x].begin(), v[x].end(), r) - 1 - std::lower_bound(v[x].begin(), v[x].end(), l) + 1;
		if(sum <= (r - l + 2) / 2) std::cout << "1\n";
		else {
			std::cout << 1 + (sum - (r - l + 1 - sum + 1)) << "\n";
		}
	}

	return 0;
}

F

posted @ 2024-08-06 21:53  Fire_Raku  阅读(7)  评论(0编辑  收藏  举报