"蔚来杯"2022牛客暑期多校训练营6 题解
A. Array
发现 \(\sum{\frac{1}{a_i}} \leq \frac{1}{2}\),考虑只保留 \(a_i\) 中二进制的最高位,并设其为 \(2^{p_i}\),此时满足 \(\sum{\frac{1}{a_i}} \leq 1\)。
考虑将问题转化为每 \(a_i\) 个数一定要有一个 \(i\) 。可设 \(m = \max(p_i)\),则将其设为答案长度,按照下列算法所得的答案一定合法。
算法:
① 对 \(a_i\) 从小到大排序。
② 对于每个 \(a_i\),我们考虑找第一个空位置(设其为 \(pos\))填充进去,并将 \(pos += a_i\) 一直填充下去,直到 \(pos>m\)。
这边可以保证被填充的位置一定为空,证明如下:
对于 \(j>i\),有 \(a_j \% a_i == 0\)。设被填充的位置 \(pos_j+k_1*a_j\) 不为空,有 \(pos_j+k_1*a_j==pos_i+k_2*a_i\),则 \((pos_j-pos_i) \% a_i==0\)。
由于 \(j>i\),从而有 \(pos_j=pos_i+k*a_i\)。
那么 \(pos_j\) 应该被填充,与原算法流程不符。
③ 对于剩下的空位置,随便填充一个1到n的数字即可。
#include<bits/stdc++.h>
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;
const int MAXN = 1e6 + 5;
const int MOD = 998244353;
int n, m, pos, ans[MAXN];
pii a[MAXN];
signed main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i].fi);
int x;
while(a[i].fi) {
x = ( a[i].fi & (-a[i].fi) );
a[i].fi -= x;
}
a[i].fi = x, a[i].se = i;
}
sort(a + 1, a + 1 + n);
m = a[n].fi, pos = 1;
for(int i = 1; i <= n; i++) {
while(ans[pos]) pos++;
for(int j = pos; j <= m; j += a[i].fi) ans[j] = a[i].se;
}
for(int i = 1; i <= m; i++) if(!ans[i]) ans[i] = 1;
printf("%d\n", m);
for(int i = 1; i <= m; i++) printf("%d ", ans[i]);
return 0;
}
B. Eezie and Pie
由于题目规定每个节点只能往上走不能往下走,我们可以预处理出所有点的深度并和 \(d[i]\) 取最小值 \(minn[i]\),那节点 \(i\) 最远可以送到 \(minn[i]\) 级父亲,用倍增预处理祖先,树上差分将这条链上的每一个节点答案+1即可。
#include<bits/stdc++.h>
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;
const int MAXN = 2e6 + 5;
int n, dep[MAXN], fa[MAXN][30], val[MAXN], d[MAXN];
vector<int> G[MAXN];
void DFS(int u, int f) {
fa[u][0] = f;
for(int i = 1; i <= 25; i++) fa[u][i] = fa[ fa[u][i - 1] ][i - 1];
dep[u] = dep[f] + 1;
for(auto v : G[u]) {
if(v == f) continue;
DFS(v, u);
}
}
int LCA(int u, int v) {
if(dep[u] > dep[v]) swap(u, v);
for(int i = 25; i >= 0; i--) {
if((1 << i) & (dep[v] - dep[u])) v = fa[v][i];
}
for(int i = 25; i >= 0; i--) {
if(fa[u][i] != fa[v][i]) {
u = fa[u][i], v = fa[v][i];
}
}
return u == v ? u : fa[u][0];
}
int Find(int u, int d) {
for(int i = 0; i <= 25; i++) if(d & (1 << i)) u = fa[u][i];
if(!u) u = 1;
return u;
}
void Add(int u, int v, int k) {
val[u] += k, val[ fa[v][0] ] -= k;
}
void DFS2(int u, int f) {
for(auto v : G[u]) {
if(v == f) continue;
DFS2(v, u);
val[u] += val[v];
}
}
signed main()
{
scanf("%d", &n);
for(int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i = 1; i <= n; i++) scanf("%d", &d[i]);
DFS(1, 0);
for(int i = 1; i <= n; i++) Add(i, Find(i, min(dep[i], d[i]) ), 1);
DFS2(1, 0);
for(int i = 1; i <= n; i++) printf("%d ", val[i]);
return 0;
}
C. Forest
考虑单独计算每条边对答案的贡献,先对所有边按权值从小到大排序。
考虑对于一条边 \((u, v)\) ,有多少生成子图满足其最小生成森林中有这条边。
可知当且仅当选择一些权值小于当前边的边加入生成子图,使得 \(u,v\) 连通时,最小生成森林不存在 \((u,v)\)。
设 \(f[i][S]\) 表示使用前 \(i\) 条边,存在一个点集为 \(S\) 的连通块的生成子图数量;\(g[i][S]\) 表示点集为 \(S\) 的生成子图数量。\(U\) 为全集。
则对于最小生成森林不存在编号为 \(i\) 的边 \((u,v)\) 的情况,有:
考虑如何计算 \(f,g\)。
对于 \(g\),我们枚举所有集合 \(S\),对于满足 \(u,v \in S\) 的集合有 \(g[i][S] = g[i-1][S]*2\),其余集合有 \(g[i][S] = g[i - 1][S]\)。
对于 \(f\),我们枚举所有集合 \(S\),对于满足 \(u,v \in S\) 的集合有 \(f[i][S] = f[i-1][S]*2+\sum f[i - 1][T] * f[i - 1][S - T]\)。其中 \(T\) 为 \(S\) 的子集,且满足 \(u \in T, v \in S-T\) 或 \(v \in T, u \in S-T\)。
其余集合有 \(f[i][S] = f[i - 1][S]\)。
暴力枚举子集求解 \(f\),设 \(S\) 中有 \(k\) 个 1,则需要枚举 \(2^k\) 个子集,对于有 \(n\) 个状态的集合 \(S\),有 \(\C_{n}^{k}\) 个集合满足集合中有 \(k\) 个 1。
单次枚举子集求解 \(f\) 的时间复杂度
总时间复杂度 \(O(m \cdot 3^n)\)
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;
const int MOD = 998244353;
int n, sze, ans, a[25][25], pow2[105], f[105][(1 << 16) + 5], g[105][(1 << 16) + 5];
vector<pii> vec;
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) cin >> a[i][j];
}
for(int i = 0; i < n; i++) {
for(int j = i + 1; j < n; j++) {
if(a[i][j]) vec.push_back({a[i][j], i * n + j});
}
}
sort(vec.begin(), vec.end());
sze = vec.size(), ans = 0;
pow2[0] = 1;
for(int i = 1; i <= sze; i++) pow2[i] = pow2[i - 1] * 2 % MOD;
for(int i = 0; i < n; i++) f[0][1 << i] = 1;
for(int s = 0; s < (1 << n); s++) g[0][s] = 1;
for(int i = 0; i < sze; i++) {
int cnt = pow2[i];
int u = vec[i].se / n, v = vec[i].se % n, w = vec[i].fi;
for(int s = 0; s < (1 << n); s++) {
if( !(s & (1 << u) ) ) continue;
if( !(s & (1 << v) ) ) continue;
cnt -= f[i][s] * g[i][((1 << n) - 1) - s] % MOD;
cnt += MOD, cnt %= MOD;
}
ans += w * cnt % MOD * pow2[sze - 1 - i] % MOD;
ans %= MOD;
for(int s = 0; s < (1 << n); s++) {
f[i + 1][s] = f[i][s];
g[i + 1][s] = g[i][s];
if( !(s & (1 << u) ) ) continue;
if( !(s & (1 << v) ) ) continue;
f[i + 1][s] = f[i][s] * 2 % MOD;
g[i + 1][s] = g[i][s] * 2 % MOD;
for(int t = (s - 1) & s; t > s - t; t = (t - 1) & s) {
if( ( (t >> u) & 1 ) ^ ( (t >> v) & 1 ) ) {
f[i + 1][s] += f[i][t] * f[i][s - t] % MOD;
f[i + 1][s] %= MOD;
}
}
}
}
cout << ans << "\n";
return 0;
}
G. Icon Design
对着题意直接模拟即可。
#include<bits/stdc++.h>
#define PIII pair< pair<int, int>, int >
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;
int n;
char ch[1005][1005];
void Print() {
for(int i = 1; i <= 4 * n + 5; i++) {
for(int j = 1; j <= 13 * n + 19; j++) printf("%c", ch[i][j]);
printf("\n");
}
}
signed main()
{
scanf("%d", &n);
for(int j = 1; j <= 13 * n + 19; j++) ch[1][j] = ch[ 4 * n + 5 ][j] = '*';
for(int i = 2; i < 4 * n + 5; i++) {
ch[i][1] = ch[i][13 * n + 19] = '*';
for(int j = 2; j < 13 * n + 19; j++) ch[i][j] = '.';
}
for(int j = n + 2; j <= 4 * n + 6 - (n + 2); j++) {
vector<int> vec;
vec.push_back(1 + n + 1 + 1);
vec.push_back(1 + n + 1 + 2 * n + 3);
vec.push_back(1 + n + 1 + 2 * n + 3 + n + 1 + 1);
vec.push_back(1 + n + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1 + 1);
for(auto i : vec) ch[j][i] = '@';
}
//Print();
// N
pii s = {1 + n + 1, 1 + n + 1 + 1};
for(int i = 1 + n + 1 + 1; i <= 1 + n + 1 + 2 * n + 3; i++) ch[s.fi][s.se] = '@', s.fi++, s.se++;
// F
s = {1 + n + 1, 1 + n + 1 + 2 * n + 3 + n + 1 + 1};
for(int k = 1; k <= 2 * n + 3; k++) {
ch[s.fi][s.se] = ch[s.fi + n + 1][s.se] = '@';
s.se++;
}
//Print();
// L
s = {4 * n + 5 - n - 1, 1 + n + 1 + 2 * n + 3 + n + 1 + 1 + 2 * n + 3 + n + 1};
for(int k = 1; k <= 2 * n + 3; k++) {
ch[s.fi][s.se] = '@';
s.se++;
}
//Print();
// S
s = {1 + n + 1, 1 + n + 1 + 2 * n + 3 + n + 1 + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1};
for(int k = 1; k <= 2 * n + 3; k++) {
ch[s.fi][s.se] = ch[s.fi + n + 1][s.se] = ch[s.fi + n + 1 + n + 1][s.se] = '@';
s.se++;
}
//Print();
s = {1 + n + 1, 1 + n + 1 + 2 * n + 3 + n + 1 + 1 + 2 * n + 3 + n + 1 + 2 * n + 3 + n + 1};
for(int k = 1; k <= n + 2; k++) {
ch[s.fi][s.se] = ch[s.fi + n + 1][s.se + 2 * n + 3 - 1] = '@';
s.fi++;
}
Print();
return 0;
}

浙公网安备 33010602011771号