2020杭电多校第六场题解

2020 Multi-University Training Contest 6


施工中。。。

1001 Road To The 3rd Building

求得每个数的几率,得到相应的差分公式,然后求两次差分即可。

比赛时一发过。

#include<bits/stdc++.h>
#define ll long long
#define maxn 400010
#define mod 1000000007
using namespace std;
ll poow(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) {
	return poow(x, mod - 2);
}
ll inv[maxn], ans[maxn], a[maxn];
int main() {
	inv[0] = 1;
	for (int i = 1; i < 200010; i++) inv[i] = po(i);
	int t;
	scanf("%d", &t);
	while (t--) {
		int n;
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), ans[i] = 0;
		for (int i = 1; i <= n / 2; i++) {
			ans[1] += inv[i];
			ans[i + 1] -= inv[i];
			ans[n + 2 - i] -= inv[i];
			ans[1] += inv[n + 1 - i];
			ans[i + 1] -= inv[n + 1 - i];
			ans[n + 2 - i] -= inv[n + 1 - i];
			ans[1] %= mod;
			ans[i + 1] %= mod;
			ans[n + 2 - i] %= mod;
		}
		if (n % 2) {
			ans[1] += inv[(n + 1) / 2];
			ans[1] %= mod;
			ans[(n + 1) / 2 + 1] -= 2 * inv[(n + 1) / 2];
			ans[(n + 1) / 2 + 1] %= mod;
		}
		for (int i = 2; i <= n; i++) ans[i] = (ans[i - 1] + ans[i]) % mod;
		for (int i = 2; i <= n; i++) ans[i] = (ans[i - 1] + ans[i]) % mod;
		ll sum = 0;
		for (int i = 1; i <= n; i++) sum = (sum + ans[i] * a[i] % mod) % mod;
		while (sum < 0) sum += mod;
		sum = sum * inv[n] % mod * inv[n + 1] % mod * 2 % mod;
		printf("%lld\n", sum);
	}
	return 0;
}

1002 Little Rabbit's Equation

纯粹模拟,切忌输出1(WA的教训)。

比赛时由于输出1,导致WA3,并且debug了很久。

#include<bits/stdc++.h>
#define ll long long
#define maxn 200010
#define mod 998244353
using namespace std;
char a[200];
int main() {
	map<char, int>mp;
	for (int i = 0; i < 10; i++) {
		mp['0' + i] = i;
	}
	for (int i = 0; i < 6; i++) {
		mp['A' + i] = 10 + i;
	}
	while (scanf("%s", a) != EOF) {
		int n = strlen(a);
		vector<int>s1, s2, s3;
		int cnt = 0, ma = 1;
		while (cnt < n) {
			if (!mp.count(a[cnt])) break;
			s1.push_back(mp[a[cnt]]);
			ma = max(ma, mp[a[cnt]]);
			cnt++;
		}
		char pe = a[cnt++];
		while (cnt < n) {
			if (!mp.count(a[cnt])) break;
			s2.push_back(mp[a[cnt]]);
			ma = max(ma, mp[a[cnt]]);
			cnt++;
		}
		cnt++;
		while (cnt < n) {
			if (!mp.count(a[cnt])) break;
			s3.push_back(mp[a[cnt]]);
			ma = max(ma, mp[a[cnt]]);
			cnt++;
		}
		int ans = -1;
		for (int i = ma + 1; i <= 16 && ans == -1; i++) {
			__int128 x = 0, y = 0, z = 0;
			for (int j = 0; j < s1.size(); j++) {
				x = x * i + s1[j];
			}
			for (int j = 0; j < s2.size(); j++) {
				y = y * i + s2[j];
			}
			for (int j = 0; j < s3.size(); j++) {
				z = z * i + s3[j];
			}
			if (pe == '+') {
				if (x + y == z) ans = i;
			}
			else if (pe == '-') {
				if (x - y == z) ans = i;
			}
			else if (pe == '*') {
				if (x * y == z) ans = i;
			}
			else if (pe == '/') {
				if (y * z == x) ans = i;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

1003 Borrow

dp+概率+组合数,还有注意某些情况有重复计算。

博主补完题后神志不清,暂时无法解释原理。

比赛时想到了是由二维DP的递推式转换得到的,但没想到要和组合数结合从而不会TLE。

#include<bits/stdc++.h>
#define ll long long
#define maxn 1000010
#define mod 998244353
using namespace std;
ll dp[maxn][2];
ll fac[maxn], inv[maxn], in2[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;
}
ll C(int n, int m) {
	return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int main() {
	fac[0] = inv[0] = 1;
	for (int i = 1; i < maxn; i++) {
		fac[i] = fac[i - 1] * i % mod;
	}
	inv[maxn - 1] = po(fac[maxn - 1]);
	for (int i = maxn - 2; i >= 1; i--) {
		inv[i] = inv[i + 1] * (i + 1) % mod;
	}
	dp[0][0] = 0;
	ll p2 = po(2);
	in2[0] = 1;
	for (int i = 1; i < maxn; i++) {
		in2[i] = in2[i - 1] * p2 % mod;
	}
	for (int i = 1; i < maxn; i++) {
		dp[i][0] = 2 + dp[i - 1][1];
		dp[i][1] = 2 + dp[i - 2][0];
	}
	int t;
	scanf("%d", &t);
	while (t--) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		if ((x + y + z) % 3) {
			printf("-1\n");
			continue;
		}
		if (x > y) swap(x, y);
		if (y > z) swap(y, z);
		if (x > y) swap(x, y);
		x = z - x, y = z - y;
		if (y == 0 || y == 1) {
			printf("%lld\n", dp[x][y]);
			continue;
		}
		ll ans = 0;
		for (int i = x; i >= 2; i--) {
			if ((x - i + y - 1) % 3 == 0 && (x - i + y - 1) / 3 <= x - i && (x - i + y - 1) / 3 <= y - 1) {
				ll sum = (x - i + y - 1) / 3;
				ll num = x - i - sum;
				ans = (ans + (sum + dp[i][1]) % mod * C(sum, num) % mod * in2[sum] % mod) % mod;
			}
			else if ((x - i + y) % 3 == 0 && (x - i + y) / 3 <= x - i && (x - i + y) / 3 <= y) {
				ll sum = (x - i + y) / 3;
				ll num = x - i - sum;
				ll cu = C(sum, num);
				if (num != 0) cu -= C(sum - 1, num - 1);
				ans = (ans + (cu * in2[sum] % mod) * (sum + dp[i][0]) % mod) % mod;
			}
		}
		for (int i = y; i >= 2; i--) {
			if ((y - i + x - 1) % 3 == 0 && (y - i + x - 1) / 3 <= y - i && (y - i + x - 1) / 3 <= x - 1) {
				ll sum = (x - i + y - 1) / 3;
				ll num = y - i - sum;
				ans = (ans + C(sum, num) * (sum + dp[i][1]) % mod * in2[sum] % mod) % mod;
			}
			else if ((x - i + y) % 3 == 0 && (x - i + y) / 3 <= y - i && (x - i + y) / 3 <= x) {
				ll sum = (x - i + y) / 3;
				ll num = y - i - sum;
				ll cu = C(sum, num);
				if (num != 0) cu -= C(sum - 1, num - 1);
				ans = (ans + (cu * in2[sum] % mod) * (sum + dp[i][0]) % mod) % mod;
			}
		}
		while (ans < 0) ans += mod;
		printf("%lld\n", ans);
	}
	return 0;
}

1005 Fragrant numbers

赛时打表跑数小时,喜提WA。

赛后补题,区间DP,跑得飞快。

题目需要注意的无非就是一个区间中能组合出哪些数。

比赛时就是没想到要区间处理,一直用一维DP做,所以得不到正解。

#include<bits/stdc++.h>
#define ll long long
#define maxn 400010
#define mod 1000000007
using namespace std;
int a[10] = { 1,1,4,5,1,4,1,9,1,9 };
int pe[20][20][100010];
int dp[5010];
int sol(int l, int r) {
	int sum = 0;
	for (int i = l; i <= r; i++) {
		sum = sum * 10 + a[(i - 1) % 10];
	}
	return sum;
}
int main() {
	memset(dp, -1, sizeof(dp));
	for (int i = 1; i <= 13; i++) {
		pe[i][i][a[(i - 1) % 10]] = 1;
	}
	for (int len = 1; len < 13; len++) {
		for (int i = 1; i + len <= 13; i++) {
			int j = i + len;
			if (len < 4) {
				int x = sol(i, j);
				if (x <= 5000) pe[i][j][x] = 1;
			}
			for (int mid = i; mid < j; mid++) {
				for (int k = 1; k <= 5000; k++) {
					if (pe[i][mid][k] == 0) continue;
					for (int u = 1; u <= 5000; u++) {
						if (pe[mid + 1][j][u] == 0) continue;
						if (k + u <= 5000) pe[i][j][k + u] = 1;
						if (k * u <= 5000) pe[i][j][k * u] = 1;
					}
				}
			}
		}
	}
	for (int i = 13; i >= 1; i--) {
		for (int j = 1; j <= 5000; j++) {
			if (pe[1][i][j]) dp[j] = i;
		}
	}
	int t;
	scanf("%d", &t);
	while (t--) {
		int x;
		scanf("%d", &x);
		printf("%d\n", dp[x]);
	}
	return 0;
}

1006 Paperfolding

由于每条边权都是 \(2^i\)\(i\) 不相同

所以任意两点之间的最小距离就是最小生成树上的距离

(口胡:若两点之间存在更短的不在最小生成树上的路径和最小生成树定义矛盾)

在最小生成树上做计数,\(n\_size[i][j]\) 表示以 \(i\) 为根的子树中 \(j\) 型点的个数,\(sum[i][j]\) 表示以 \(i\) 为根的子树中所有 \(j\) 型点到 \(i\) 的总边权

对于每个 \(i\) ,统计每个子树和其他子树之间的 \(0,1\) 点距离

比赛时由于儿子间处理出现问题导致WA1,在和队友debug后发现问题并解决。

#include <bits/stdc++.h>
#pragma warning (disable:4996)
using namespace std;

typedef long long LL;
const int maxn = 1e5 + 5;
const LL mod = 1e9 + 7;
struct Line {
	int u, v;
	LL w;
}line[maxn * 2];
int f[maxn];
int find(int x) {
	while (x != f[x])
		x = f[x] = f[f[x]];
	return x;
}

typedef pair<int, LL>pii;
vector<pii> E[maxn];

int a[maxn];
LL n_size[maxn][2], sum[maxn][2];

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int n, m;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]);
		int u, v;
		LL w = 1;
		for (int i = 1; i <= m; i++) {
			scanf("%d%d", &line[i].u, &line[i].v);
			w = w * 2 % mod;
			line[i].w = w;
		}

		int limit = n;
		for (int i = 1; i <= n; i++) {
			f[i] = i;
			E[i].clear();
		}
		for (int i = 1; i <= m; i++) {
			int eu = find(line[i].u);
			int ev = find(line[i].v);
			if (eu != ev) {
				f[eu] = ev;
				E[line[i].u].push_back(make_pair(line[i].v, line[i].w));
				E[line[i].v].push_back(make_pair(line[i].u, line[i].w));
				limit--;
				if (limit == 1)
					break;
			}
		}

		LL ans = 0;
		function<void(int, int)> dfs;
		dfs = [&](int now, int fa) {
			n_size[now][0] = n_size[now][1] = 0;
			sum[now][0] = sum[now][1] = 0;
			n_size[now][a[now]] = 1;

			for (auto it : E[now]) {
				if (it.first == fa)
					continue;
				dfs(it.first, now);
				n_size[now][0] += n_size[it.first][0];
				sum[now][0] = (sum[now][0] + sum[it.first][0] + n_size[it.first][0] * it.second % mod) % mod;
				n_size[now][1] += n_size[it.first][1];
				sum[now][1] = (sum[now][1] + sum[it.first][1] + n_size[it.first][1] * it.second % mod) % mod;
			}

			for (auto it : E[now]) {
				if (it.first == fa)
					continue;
				ans = (ans + (sum[it.first][0] + n_size[it.first][0] * it.second % mod) % mod * (n_size[now][1] - n_size[it.first][1]) % mod
					+ (sum[it.first][1] + n_size[it.first][1] * it.second % mod) % mod * (n_size[now][0] - n_size[it.first][0]) % mod) % mod;
			}
		};
		dfs(1, 0);

		printf("%lld\n", ans);
	}
	return 0;
}

1009 Divisibility

本题题意为对于任意 \(b\) 进制数,是否只要各位数和整除 \(x\), 则本身整除 \(x\)

\[(c_0+\cdots+c_{n-1}+c_{n}) \mod x \equiv 0\\ (c_0*b_n+\cdots+c_{n-1}*b+c_n*b^0) \mod x \equiv 0 \]

易得

\[b^n \mod x = 1 \\ b \mod x = 1 \]

比赛时盲猜两发规律,然后WA2,直到队友给出正解。

#include <bits/stdc++.h>
#pragma warning (disable:4996)
using namespace std;

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		long long b, x;
		scanf("%lld%lld", &b, &x);
		if (b <= x) printf("F\n");
		else if (b % x == 1) printf("T\n");
		else printf("F\n");
	}
	return 0;
}

1010 Expectation

矩阵树(最小生成树计数)的模板题

例题

对于一个无向图 G ,它的生成树个数等于其基尔霍夫 Kirchhoff 矩阵任何一个 N - 1 阶主子式的行列式的绝对值。

对于单个二进制位,只保留边权该二进制位上为 \(1\) 的边,则该图上所有生成树权值都包含 \(2^i\)

对于每个二进制位,重新建图统计生成树个数,计算总权值除总生成树个数即可

比赛时由于矩阵树模板使用不熟练,导致WA1,找出错误后解决。

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

typedef long long LL;
const int maxn = 1e4 + 5;
const LL mod = 998244353;
struct Line {
	int u, v;
	LL w;
} line[maxn];

LL quickpow(LL m, LL p) {
	LL res = 1;
	while (p) {
		if (p & 1)
			res = res * m % mod;
		m = m * m % mod;
		p >>= 1;
	}
	return res;
}

namespace Matrix {
	const int maxn = 105;
	LL a[maxn][maxn];
	LL ans;
	void work(int n) {
		for (int i = 2, inv, tmp; i <= n; ++i) {
			for (int j = i + 1; j <= n; ++j)
				if (!a[i][i] && a[j][i]) {
					ans = -ans;
					swap(a[i], a[j]);
					break;
				}
			inv = quickpow(a[i][i], mod - 2);
			for (int j = i + 1; j <= n; ++j) {
				tmp = (LL)a[j][i] * inv % mod;
				for (int k = i; k <= n; ++k)
					a[j][k] = (a[j][k] - (LL)a[i][k] * tmp % mod) % mod;
			}
		}
	}
	LL slove(int n, int m, LL limit) {
		ans = 1;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				a[i][j] = 0;
		for (int i = 1; i <= m; ++i) {
			if (!(line[i].w & limit))
				continue;
			int x = line[i].u, y = line[i].v;
			a[x][x]++;
			a[y][y]++;
			a[x][y]--;
			a[y][x]--;
		}
		work(n);
		for (int i = 2; i <= n; ++i)
			ans = (LL)ans * a[i][i] % mod;
		return (ans % mod + mod) % mod;
	}
} // namespace Matrix

int main() {

	int t;
	scanf("%d", &t);
	while (t--) {
		int n, m;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= m; i++)
			scanf("%d%d%lld", &line[i].u, &line[i].v, &line[i].w);

		LL ans = 0;
		for (int i = 0; i <= 31; i++)
			ans = (ans + (1 << i) % mod * Matrix::slove(n, m, (1 << i)) % mod) %
			mod;
		LL inv = Matrix::slove(n, m, (LL(1) << 32) - 1);
		inv = quickpow(inv, mod - 2);
		ans = ans * inv % mod;
		printf("%lld\n", ans);
	}

	return 0;
}
posted @ 2020-08-07 14:21  st1vdy  阅读(322)  评论(1编辑  收藏  举报