Contest Info
Solved | A | B | C | D | E | F | G | H | I | J | K | L | M |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
6 / 13 | O | - | O | - | - | - | Ø | Ø | O | - | - | Ø | - |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A. Tetrahedron
签到。
C. Boring Game
找到规律后直接模拟即可。
规律是多种多样的。
Code
// Author : heyuhhh
// Created Time : 2020/08/04 13:57:50
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<vector<int>> mytype;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]
";}
template<typename T, typename V>
void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 1e6 + 5;
int n, k;
int p[N];
mytype cz(vector<int>& a, vector<int>&b, int Max) {
mytype res;
vector<int> t1 = b, t2 = a;
for (int i = 0; i < 2 * n; i++) {
a[i] += Max;
b[i] += Max;
}
res.push_back(a);
res.push_back(t1);
res.push_back(t2);
res.push_back(b);
return res;
}
void gao(mytype& a, int k, int Max) {
if (k == 0) return;
vector<mytype> res;
for (int i = 0; i < sz(a); i += 2) {
res.push_back(cz(a[i], a[i + 1], Max));
}
a.clear();
for (auto& it : res) {
for (auto& it2 : it) {
a.push_back(it2);
}
}
gao(a, k - 1, Max * 2);
}
void run() {
cin >> n >> k;
int tot = 2 * n;
for (int i = 1; i <= k; i++) {
tot *= 2;
}
for (int i = 1; i <= tot; i++) {
cin >> p[i];
}
mytype a;
a.resize(2);
a[0].resize(2 * n), a[1].resize(2 * n);
for (int i = 0; i < 2 * n; i++) {
a[0][i] = 2 * n - i;
a[1][i] = 2 * n + 1 + i;
}
--k;
gao(a, k, 4 * n);
for (int i = 0; i < 2 * n; i++) {
for (int j = 0; j < sz(a); j++) {
cout << p[a[j][i]];
if (--tot > 0) {
cout << ' ';
}
}
}
cout << '
';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
G. Tree
与选择边数有关的树形(dp),一般的写法就是一开始钦定与父亲连着的拿一条边要选,这样方便转移。最后统计答案断开这条边即可。
这个题设(dp[i][0/1])表示以(i)为根节点的子树中,是否存在一个点其度数不受到限制,并且(i)与其父亲这条边已选的最大边权和。
那么最后的答案就是(dp[i][1])断开父亲这条边的最大值,但是注意与父亲断开过后可能还能够多选择一个儿子,这种情况再单独考虑一下即可。
因为dp转移时要贪心进行转移,所以代码有点细节:
Code
// Author : heyuhhh
// Created Time : 2020/08/04 19:16:59
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]
";}
template<typename T, typename V>
void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 1e5 + 5;
void run() {
int n, k;
cin >> n >> k;
vector<vector<pii>> G(n);
vector<int> d(n);
ll sum = 0;
for (int i = 0; i < n - 1; i++) {
int u, v, w;
cin >> u >> v >> w;
sum += w;
--u, --v;
G[u].push_back(MP(v, w));
G[v].push_back(MP(u, w));
++d[u], ++d[v];
}
if (*max_element(all(d)) <= k) {
cout << sum << '
';
return;
}
if (k == 0) {
cout << 0 << '
';
return;
}
if (k == 1) {
ll ans = 0;
for (int i = 0; i < n; i++) {
ll res = 0;
for (auto it : G[i]) {
int v = it.fi, w = it.se;
res += w;
}
ans = max(ans, res);
}
cout << ans << '
';
return;
}
vector<vector<ll>> dp(n, vector<ll>(2));
function<ll(int, int, int)> calc = [&](int u, int c, int fa) {
vector<pair<ll, pair<int, int>>> dp0;
for (auto it : G[u]) {
int v = it.fi, w = it.se;
if (v != fa) {
dp0.push_back(MP(dp[v][0] + w, MP(v, w)));
}
}
sort(dp0.rbegin(), dp0.rend());
ll res = 0, Max = -1e18;
for (int i = 0; i < min(sz(dp0), c); i++) {
res += dp0[i].fi;
}
for (int i = c; i < sz(dp0); i++) {
int v = dp0[i].se.fi, w = dp0[i].se.se;
Max = max(Max, dp[v][1] + w);
}
if (sz(dp0) >= c + 1) {
for (int i = 0; i < c; i++) {
int v = dp0[i].se.fi;
Max = max(Max, -dp[v][0] + dp[v][1] + dp0[c].fi);
}
} else {
for (int i = 0; i < sz(dp0); i++) {
int v = dp0[i].se.fi;
Max = max(Max, -dp[v][0] + dp[v][1]);
}
}
res += Max;
return res;
};
ll ans = 0;
function<void(int, int)> dfs = [&](int u, int fa) {
vector<pair<ll, int>> dp0;
for (auto it : G[u]) {
int v = it.fi, w = it.se;
if (v != fa) {
dfs(v, u);
dp[u][1] += dp[v][0] + w;
dp0.push_back(MP(dp[v][0] + w, v));
}
}
sort(dp0.rbegin(), dp0.rend());
for (int i = 0; i < min(sz(dp0), k - 1); i++) {
dp[u][0] += dp0[i].fi;
}
dp[u][1] = max(dp[u][1], calc(u, k - 2, fa));
ans = max(ans, max(dp[u][1], calc(u, k - 1, fa)));
};
dfs(0, 0);
cout << ans << '
';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
freopen("output.out", "w", stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
H. Set1
题意:
同Set2类似,不过是每次删除最小的一个后再随机删除(k-1)个。如果个数小于(k)就不删除。然后问每个数剩下的概率。
范围有点不同:(nleq 5000)。
思路:
首先看范围就能明确这个题可以使用(O(n^2))的做法,一般就是dp。
这个题可以类似于约瑟夫环的删除那样去思考,我们定义(dp[n][i])表示最后还有(n)个数,第(i)个最终没被删去的概率。那么接下来就开始删,首先第一个默认要被删去,然后会随机进行删除,假设左边删掉(t)个,那么(i)这个位置就会变到(i-t-1)这个位置。后面的是没影响的。据此我们可以写出(dp)转移式。
初一看复杂度是(O(n^2k))的,但实际上对于第一维只有(O(frac{n}{k}))个状态有用。
那么直接跑上述dp复杂度实际上是(O(n^2))的。
Code
// Author : heyuhhh
// Created Time : 2020/08/05 16:45:03
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5000 + 5, MOD = 998244353;
int qpow(ll a, ll b) {
ll res = 1;
while(b) {
if(b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int fac[N], inv[N];
void init() {
fac[0] = 1;
for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
inv[N - 1] = qpow(fac[N - 1], MOD - 2);
for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}
int C(int n, int m) {
return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
void run() {
int n, k;
cin >> n >> k;
if (n <= k) {
for (int i = 1; i <= n; i++) {
cout << 1 << "
"[i == n];
}
return;
}
int r = n % (k + 1);
if (r == 0) {
for (int i = 1; i <= n; i++) {
cout << 0 << "
"[i == n];
}
return;
}
vector<int> ans(n + 1);
vector<vector<int>> dp(n + 1, vector<int>(n + 1));
for (int i = 1; i <= r; i++) {
dp[r][i] = 1;
}
vector<int> pre(n + 1);
for (int i = k; i <= n; i++) {
pre[i] = qpow(C(i, k), MOD - 2);
}
for (int i = r + k + 1; i <= n; i += k + 1) {
for (int j = 2; j <= i; j++) {
int L = j - 2, R = i - j;
for (int t = 0; t <= k; t++) {
if (t <= L && k - t <= R) {
dp[i][j] = (dp[i][j] + 1ll * C(L, t) * C(R, k - t) % MOD * pre[i - 1] % MOD
* dp[i - k - 1][j - t - 1] % MOD) % MOD;
}
}
}
}
for (int i = 1; i <= n; i++) {
cout << dp[n][i] << "
"[i == n];
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
init();
int T; cin >> T; while(T--)
run();
return 0;
}
I. Paperfolding
显然我们可以将所有的折纸方案数归为只有两种的情况。
并且每折一次,对应方向的折现都会翻倍。
因为求的是期望,所以与顺序无关,我们可以直接枚举横向折多少次、竖向折多少次,然后直接根据期望定义统计答案即可。
Code
// Author : heyuhhh
// Created Time : 2020/08/04 12:49:25
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 998244353;
int qpow(ll a, ll b) {
ll res = 1;
while(b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
void run() {
ll n;
cin >> n;
int res = 1ll * 2 * qpow(3, n) % MOD;
res = (res + qpow(2, n)) % MOD;
res = (res + qpow(4, n)) % MOD;
res = 1ll * res * qpow(qpow(2, n), MOD - 2) % MOD;
cout << res << '
';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
L. Set1
题意:
给定一个(1)~(n)的集合,保证(n)为奇数。
然后每次首先删除最小的一个数,之后再在剩下的数中随机删除一个数直到还剩下(1)个数。
现在对于每个数(i),回答数(i)剩下的概率。
思路:
令(p=frac{n}{2}),首先有观察:
- 对于一个数(i),删除的最小的(p)个数一定在他左边,他右边的数一定是被随机删掉的。
接下来考虑枚举所有情况,现在我们知道其右边有(n-i)个数,他们一定会和左边的(n-i)个一操作进行配对,然后剩下的两两进行配对。
先考虑第一种,左右互相配对的方案数:也就是我们要从左边选出(n-i)个位置,然后还要乘以一个阶乘,即为({i-1choose n-i}cdot (n-i)!)。
第二种就是剩下(i-1-(n-i))个两两匹配,那么答案就是(sum_{j=0}{i-1-(n-i)-2jchoose 2})。注意最后还要除以一个阶乘,因为顺序是无关的。
那么将两者乘起来就是答案。
Code
// Author : heyuhhh
// Created Time : 2020/08/04 16:15:23
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5e6 + 5, MOD = 998244353;
int qpow(ll a, ll b) {
ll res = 1;
while(b) {
if(b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int fac[N], inv[N];
void init() {
fac[0] = 1;
for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
inv[N - 1] = qpow(fac[N - 1], MOD - 2);
for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}
int C(int n, int m) {
return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
int cat(int n) {
if (n == 0) return 1;
return 1ll * C(2 * n, n) * qpow(n + 1, MOD - 2) % MOD;
}
int ans[N];
void run() {
int n;
cin >> n;
int k = n / 2;
for (int i = 1; i <= k; i++) {
ans[i] = 0;
}
for (int i = k + 1; i <= n; i++) {
int p = i - 1 - (n - i);
int res = 1ll * C(i - 1, n - i) % MOD * fac[n - i] % MOD;
int res2 = 1ll * fac[p] * qpow(1ll * qpow(2, p / 2) * fac[p / 2] % MOD, MOD - 2) % MOD;
res = 1ll * res * res2 % MOD;
ans[i] = res;
}
int tot = 0;
for (int i = 1; i <= n; i++) {
tot += ans[i];
if (tot >= MOD) tot -= MOD;
}
tot = qpow(tot, MOD - 2);
for (int i = k; i <= n; i++) {
ans[i] = 1ll * ans[i] * tot % MOD;
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << "
"[i == n];
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
init();
int T; cin >> T; while(T--)
run();
return 0;
}