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}\)
#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} \]
#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
純粹找規律。
#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 - 1 個數都是 0 \\dp[\frac{n}{2}]=\frac{1}{2^{\frac{n}{2}}} \\dp[i+1]=dp[i]\times \frac{\frac{n}{2}+i}{2i} \\\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;
}