2020杭电多校第五场题解

2020 Multi-University Training Contest 5


施工中。。。

1001 Tetrahedron

已知 \(a\times b\times c\) 的四面体,以 \(a\)\(x\) 轴,\(b\)\(y\) 轴, \(c\)\(z\)

\(z\) 为轴做切面,为 \(c\times \frac{a\times b}{\sqrt{a^2+b^2}}\) 的三角形

则易知 \(h= \frac{abc}{\sqrt{a^2b^2+a^2c^c+b^2c^2}}\)

\(\frac{1}{h^2}=\frac{1}{a^2}+\frac{1}{b^2}+\frac{1}{c^2}\)\(O(n\log n)\)\(\frac{3}{n^2}\) 的期望即可

比赛时一发过。

#include<bits/stdc++.h>
#define ll long long
#define maxn 6000010
#define mod 998244353
using namespace std;
ll pe[maxn], sum[maxn];
ll po(ll x) {
	ll bas = 1, y = mod - 2;
	while (y) {
		if (y % 2) bas = bas * x % mod;
		x = x * x % mod;
		y /= 2;
	}
	return bas;
}
int main() {
	for (int i = 1; i < maxn; i++) {
		pe[i] = po(i);
		sum[i] = (sum[i - 1] + pe[i] * pe[i] % mod) % mod;
	}
	int t;
	scanf("%d", &t);
	while (t--) {
		int n;
		scanf("%d", &n);
		printf("%lld\n", 3 * sum[n] * pe[n] % mod);
	}
	return 0;
}

1003 Boring Game

手模一下一张纸的情况,多折几次就基本明白了。

每一次折纸实际上就是将当前的状态复制一份,然后翻转。

比赛时一发过。

#include <bits/stdc++.h>
using namespace std;

vector<int> p[12];
void init() {
	p[0] = vector<int>{ 1, 1 };
	for (int i = 1; i <= 10; i++) {
		p[i] = vector<int>((1 << i) + 1);
		p[i][0] = (1 << i);
		for (int j = 1; j <= (1 << (i - 1)); j++)
			p[i][(1 << (i - 1)) + j] = p[i][(1 << (i - 1)) - j + 1] = (p[i - 1][j] << 1);
		for (int j = (int)p[i].size() - 1; j >= 1; j--)
			if (j & 1)
				p[i][j]--;
	}
}

int a[500][2000];
int b[500 * 2000];
int col[2000];
int main() {
	init();
	int t;
	scanf("%d", &t);
	while (t--) {
		int n, k;
		scanf("%d%d", &n, &k);
		for (int i = 1; i <= 2 * n * (1 << k); i++)
			scanf("%d", &b[i]);
		for (int i = 1; i <= (1 << k); i++)
			col[p[k][i]] = i;

		bool flag = false;
		int now = 0;
		for (int i = 1; i <= (1 << k); i++) {
			int g = col[i];
			if (!flag) {
				for (int j = 2 * n; j >= 1; j--)
					a[j][g] = ++now;
			}
			else {
				for (int j = 1; j <= 2 * n; j++)
					a[j][g] = ++now;
			}
			flag ^= true;
		}
		for (int i = 1; i <= 2 * n - 1; i++) {
			for (int j = 1; j <= (1 << k); j++)
				printf("%d ", b[a[i][j]]);
		}
		for (int j = 1; j <= (1 << k) - 1; j++)
			printf("%d ", b[a[2 * n][j]]);
		printf("%d\n", b[a[2 * n][1 << k]]);
	}
	return 0;
}

1007 Tree

题意为求一个树上的子图,使得子图全连通、子图上度数大于 \(k\) 的点至多为 \(1\) 个、子图边权值和最大

我们任取一点为根,做树形 \(dp\),令 \(dp[i][j]\) 表示

  • \(i\) 为根的子树,度数大于 \(k\) 的点为 \(j\)
  • \(i\) 的度数小于等于 \(k-1\)\(i\) 为度数大于 \(k\) 的那个点

\(dp[i][0]\) 为子树中最大的 \(k-1\)\(dp[j][0]\)

\(dp[i][1]\) 为所有子树的 \(dp[j][0]\) 之和或 \(dp[i][0]\) 删除一个子树换成 \(dp[j][1]\)

对于每个 \(i\) 容易由 \(dp[i][0]\)\(dp[i][1]\) 得到以 \(i\) 为根的子图的最大值,求最值即可

比赛时一发过。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, LL> pii;
typedef pair<LL, LL> pll;
const int maxn = 2e5 + 5;
LL dp[maxn][2];
vector<pii> E[maxn];

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int n, k;
		scanf("%d%d", &n, &k);
		int u, v, w;
		for (int i = 1; i <= n; i++)
			E[i].resize(0);
		for (int i = 1; i < n; i++) {
			scanf("%d%d%d", &u, &v, &w);
			E[u].push_back(make_pair(v, w));
			E[v].push_back(make_pair(u, w));
		}
		if (k == 0) {
			printf("0\n");
			continue;
		}

		LL ans = 0;
		function<void(int, int)> dfs;
		dfs = [&](int now, int fa) {
			dp[now][0] = dp[now][1] = 0;
			int g = 0;
			vector<pll> a;
			a.push_back(make_pair(0, 0));
			for (auto it : E[now]) {
				if (it.first == fa)
					continue;
				dfs(it.first, now);
				a.push_back(make_pair(it.second + dp[it.first][0],
					it.second + dp[it.first][1]));
				g++;
			}
			LL ansl = 0, ansr = 0;
			sort(a.begin() + 1, a.begin() + 1 + g,
				[](const pll& a, const pll& b) { return a.first > b.first; });
			for (int i = 1; i <= min(k - 1, g); i++)
				dp[now][0] += a[i].first;
			if (g >= k)
				ansl = dp[now][0] + a[k].first;
			else
				ansl = dp[now][0];
			ans = max(ans, ansl);

			for (int i = 1; i <= g; i++)
				dp[now][1] += a[i].first;
			ansr = dp[now][1];
			for (int i = 1; i <= min(k - 1, g); i++)
				dp[now][1] =
				max(dp[now][1], dp[now][0] - a[i].first + a[i].second);
			for (int i = k; i <= g && (k - 1 > 0); i++)
				dp[now][1] =
				max(dp[now][1], dp[now][0] - a[k - 1].first + a[i].second);

			for (int i = 1; i <= min(k, g); i++)
				ansr = max(ansr, ansl - a[i].first + a[i].second);
			for (int i = k + 1; i <= g && (k > 0); i++)
				ansr = max(ansr, ansl - a[k].first + a[i].second);
			ans = max(ans, ansr);
		};
		dfs(1, 0);
		printf("%lld\n", ans);
	}
	return 0;
}

1008 Set2

该题为 \(1012\) 的变式,由于 \(n\leq 5000\),所以可以用 \(O(n^2)\)\(dp\) 解决。
\(dp[i]\) 表示倒数第 \(i\) 个存活的几率,首先要计算最后剩余数,将其 \(dp\) 值赋为 \(1\)
然后加入人数,直到 \(n\) 个人。如果这个数不是一定被取出,就做更新一次 \(dp\),否则 \(dp[i]=0\)

\[\begin{aligned}dp[i]=dp[i]\times \frac{j-i}{j}+dp[i-1]\times \frac{i-1}{j},j 为当前数字全部数量\end{aligned} \]

比赛时对剩余人数处理和题目表达的方式有出入,导致WA1,理清处理方法后就过了。

#include<bits/stdc++.h>
#define ll long long
#define maxn 100010
#define mod 998244353
using namespace std;
ll inv[maxn], ans[maxn];
ll qpow(ll x, ll y) {
	ll bas = 1;
	while (y) {
		if (y % 2) bas = bas * x % mod;
		x = x * x % mod;
		y /= 2;
	}
	return bas;
}
ll po(ll x) {
	ll bas = 1, y = mod - 2;
	while (y) {
		if (y % 2) bas = bas * x % mod;
		x = x * x % mod;
		y /= 2;
	}
	return bas;
}
int main() {
	for (int i = 1; i <= 5100; i++) {
		inv[i] = po(i);
	}
	int t;
	scanf("%d", &t);
	while (t--) {
		int n, k;
		scanf("%d%d", &n, &k);
		int res = n % (k + 1);
		for (int i = 0; i < res; i++) {
			ans[i] = 1;
		}
		for (int i = res; i < n; i++) {
			ans[i] = 0;
			if ((n - i) % (k + 1) == 1) continue;
			for (int j = i; j >= 0; j--) {
				if (j == 0) {
					ans[j] = ans[j] * (i - j) % mod * inv[i + 1] % mod;
					ans[j] %= mod;
				}
				else {
					ans[j] = ans[j] * (i - j) % mod * inv[i + 1] % mod + ans[j - 1] * (j) % mod * inv[i + 1] % mod;
					ans[j] %= mod;
				}

			}
		}
		for (int i = n - 1; i > 0; i--) printf("%lld ", ans[i]);
		printf("%lld\n", ans[0]);
	}
	return 0;
}

1009 Paperfolding

纯粹找规律(大雾)
期望为\(1+2^n+(3^n)/(2^{n-1})\)

比赛时一发过。

#include<bits/stdc++.h>
#define ll long long
#define maxn 1000010
#define mod 998244353
using namespace std;
ll qpow(ll x, ll y) {
	ll bas = 1;
	while (y) {
		if (y % 2) bas = bas * x % mod;
		x = x * x % mod;
		y /= 2;
	}
	return bas;
}
ll po(ll x) {
	ll bas = 1, y = mod - 2;
	while (y) {
		if (y % 2) bas = bas * x % mod;
		x = x * x % mod;
		y /= 2;
	}
	return bas;
}
int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		ll n;
		scanf("%lld", &n);
		if (n == 0) {
			printf("4\n");
			continue;
		}
		ll ans = qpow(2, n) + 1;
		ans %= mod;
		ans = ans + qpow(3, n) * po(qpow(2, n - 1)) % mod;
		ans %= mod;
		printf("%lld\n", ans);
	}
	return 0;
}

1012 Set1

\(1008\)\(dp\) 式可以得到正解,但 \(O(n^2)\)\(dp\)\(\text{T}\),所以找找规律。不难得出规律:

\[\begin{gather*}对于任意一个合法的n,前 \lfloor \frac{n}{2} \rfloor 个数都是 0 \\dp[\frac{n+1}{2}]=\frac{1}{2^{\frac{n}{2}}} \\dp[i+1]=dp[i]\times \frac{\frac{n}{2}+i}{2i}\ (i<n-1) \\ dp[n]=dp[n-1]\\\end{gather*} \]

比赛时一发过。

#include<bits/stdc++.h>
#define ll long long
#define maxn 1000010
#define mod 998244353
using namespace std;
ll qpow(ll x, ll y) {
	ll bas = 1;
	while (y) {
		if (y % 2) bas = bas * x % mod;
		x = x * x % mod;
		y /= 2;
	}
	return bas;
}
ll po(ll x) {
	ll bas = 1, y = mod - 2;
	while (y) {
		if (y % 2) bas = bas * x % mod;
		x = x * x % mod;
		y /= 2;
	}
	return bas;
}
int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int n;
		scanf("%d", &n);
		if (n == 1) {
			printf("1\n");
			continue;
		}
		for (int i = 1; i <= n / 2; i++) printf("0 ");
		ll ans = po(qpow(2, n / 2));
		printf("%lld ", ans);
		for (int i = 1; i < n / 2; i++) {
			ans = ans * (n / 2 + i) % mod * po(2 * i) % mod;
			printf("%lld ", ans);
		}
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2020-08-04 18:31  st1vdy  阅读(669)  评论(8编辑  收藏  举报