"蔚来杯"2022牛客暑期多校训练营6 题解

A. Array#

发现 1ai12,考虑只保留 ai 中二进制的最高位,并设其为 2pi,此时满足 1ai1

考虑将问题转化为每 ai 个数一定要有一个 i 。可设 m=max(pi),则将其设为答案长度,按照下列算法所得的答案一定合法。

算法:

① 对 ai 从小到大排序。

② 对于每个 ai,我们考虑找第一个空位置(设其为 pos)填充进去,并将 pos+=ai 一直填充下去,直到 pos>m

这边可以保证被填充的位置一定为空,证明如下:

对于 j>i,有 aj%ai==0。设被填充的位置 posj+k1aj 不为空,有 posj+k1aj==posi+k2ai,则 (posjposi)%ai==0

由于 j>i,从而有 posj=posi+kai

那么 posj 应该被填充,与原算法流程不符。

③ 对于剩下的空位置,随便填充一个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) 的情况,有:

u,vTf[i1][T]g[i1][UT]

考虑如何计算 f,g

对于 g,我们枚举所有集合 S,对于满足 u,vS 的集合有 g[i][S]=g[i1][S]2,其余集合有 g[i][S]=g[i1][S]

对于 f,我们枚举所有集合 S,对于满足 u,vS 的集合有 f[i][S]=f[i1][S]2+f[i1][T]f[i1][ST]。其中 TS 的子集,且满足 uT,vSTvT,uST

其余集合有 f[i][S]=f[i1][S]

暴力枚举子集求解 f,设 S 中有 k 个 1,则需要枚举 2k 个子集,对于有 n 个状态的集合 S,有 Cnk 个集合满足集合中有 k 个 1。

单次枚举子集求解 f 的时间复杂度

k=0nCnk2k=(1+2)n=3n

总时间复杂度 O(m3n)

#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;
}

作者:Orzjh

出处:https://www.cnblogs.com/Orzjh/p/16567543.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Orzjh  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu