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\) 。
比賽時對剩余人數處理和題目表達的方式有出入,導致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}\),所以找找規律。不難得出規律:
比賽時一發過。
#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;
}