首页 > 其他分享 >省选联考 2024

省选联考 2024

时间:2024-03-16 09:02:17浏览次数:18  
标签:ch min 省选 res 2024 int 子树 ans 联考

省选联考 2024

前言

有的题没必要一定要推到满分才可以,比较阴间的写个八九十的分就很不错了,特别阴间的写个暴力就算了,没必要一定要全学懂是不是/fad

[省选联考 2024] 季风

传送门

讲题目转化为在 \((0,0)\),求最小 \(m\) 使 \(|x-\sum\limits_{i=0}^{m-1}x_{i\mod n}|+|y-\sum\limits_{i=0}^{m-1}y_{i\mod n}|\le k\times m\)

令 \(m=n\times z+i(0≤i<n)\),\(x_i\) 的前缀和 \(X_i\),\(y_i\) 的前缀和 \(Y_i\)

原式转化为 \(|x-X_n\times z-X_i|+|y-Y_n\times z-Y_i|\le k\times(n\times z+i)\)

解绝对值不等式,更新答案即可。

void calc(int k, int b) 
{
	if (k == 0 && b < 0) l = inf + 1;
	else if (k > 0) r = min(r, (int)floor(1.0 * b / k));
	else l = max(l, (int)ceil(1.0 * b / k));
}

signed main() 
{
	int T;
	cin >> T;
	while (T--) 
	{
		cin >> n >> k >> x >> y;
		for (rint i = 1; i <= n; i++)
		{
			cin >> X[i] >> Y[i];
			X[i] += X[i - 1];
			Y[i] += Y[i - 1];			
		}
		if (!x && !y) 
		{
			puts("0");
			continue;
		}
		int ans = inf;
		for (rint i = 1; i <= n; i++) 
		{
			l = 0, r = inf;
			calc(X[n] + Y[n] - n * k, x + y - X[i] - Y[i] + i * k);
			calc(X[n] - Y[n] - n * k, x - y - X[i] + Y[i] + i * k);
			calc(Y[n] - X[n] - n * k, y - x - Y[i] + X[i] + i * k);
			calc(-X[n] - Y[n] - n * k, -x - y + X[i] + Y[i] + i * k);
			if (l <= r) ans = min(ans, n * l + i);
		}
		cout << (ans == inf ? -1 : ans) << endl;
	}
	return 0;
}

[省选联考 2024] 魔法手杖

传送门

PS:窝太菜了,调了半天还是 96pts,算法应该没有假,应该是代码实现哪里出了问题,懒得调了。

题目大概就是说给你 \(n\) 个 \([0,2^k)\) 中的整数 \(a_1,a_2,\dots,a_n\),第 \(i\) 个数对应代价 \(b_i\)。

需要选取一个 \(U=\{1,2,\dots,n\}\) 的子集 \(S\),满足 \(\sum_{i\in S} b_i\le m\),以及一个 \([0,2^k)\) 中的整数 \(x\),最大化 \(\min\{\min_{i\in S}\{a_i+x\},\min_{i\in U\setminus S}\{a_i\oplus x\}\}\)。

最大化min,异或,\(2^k\) 对于这些东西要敏感,直接考虑二分和 dp 并不可做,考虑 01Trie 是否有操作空间

好像能玩,吃碗泡面开始把玩

从高往低逐位找 \(x\) 以及最终的答案

设当前走到了树上的某个结点 \(u\),已定答案 \(k\) 且只有高位有值,\(k\) 是从根走到 \(u\) 的路径异或上 \(x\) 对应的二进制数。

判断 \(k\) 单个位数,\(k\) 的当前位是否能为 \(1\),能的话最好。讨论 \(k\) 有两个儿子的情形,一个儿子是类似的。若希望 \(k\) 的当前位为 \(1\),须将 \(u\) 一个儿子子树内的所有数都划分进加法部分(该操作简记为划分),加法部分对答案的贡献也 \(\ge k\mid 2^K\),其中 \(2^K\) 为当前位的权值。

枚举要划分哪个儿子并判断是否可行,需要儿子子树内所有数对应的 \(b_i\) 之和不超过剩余可用的代价,其次加法部分的数的最小值 \(minn\) 需要满足 \(minn+(x\mid (2^d-1))\ge k\mid 2^d\),\(x\) 的值任意,将未确定的低位全部置为了 \(1\)。如果发现划分某一个儿子可以使得答案的当前位为 \(1\),就对应的更新 \(k\) 与 \(x\),朝另一个儿子递归。

若确定了 \(k\) 的当前位不能为 \(1\),仍考虑是否要划分一棵子树并令异或部分的当前位为 \(1\),此时异或部分不会对最终的答案产生限制,只考虑加法,最优解是将剩下低位全是 \(1\),所以用算出的数更新全局答案即可。不划分子树的情况,枚举 \(x\) 的取值,此时当前位异或为 \(1\) 的子树不会对答案产生限制,朝异或为 \(0\) 的子树递归即可。

复杂度 \(O (nk)\)

int n, m, k;
int b[N], ch[M][2], tot;
int a[N], minn[M];
int res;
int s[M];

int min(int a, int b) 
{
	return a < b ? a : b;
}
int max(int a, int b) 
{
	return a > b ? a : b;
}

inline int read() 
{
	int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') 
	{
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9')
		x = x * 10 + ch - '0', ch = getchar();
	return x * f;
}

void print(int n) 
{
	if (n < 0) 
	{
		putchar('-');
		n *= -1;
	}
	if (n > 9) print(n / 10);
	putchar(n % 10 + '0');
}

void insert(int x, int t) 
{
	int p = 0;
	for (rint i = k - 1; i >= 0; i--) 
	{
		if (!ch[p][x >> i & 1]) 
		{
			ch[p][x >> i & 1] = ++tot;
			minn[tot] = inf;
			s[tot] = 0;
		}
		p = ch[p][x >> i & 1];
		minn[p] = min(minn[p], x);
		s[p] += t;
	}
}

//mr -> min
void solve(int p, int d, int mr, int w, int x, int ans) 
{
	if (!d) 
	{
		res = max(res, min(mr + x, ans));
		return ;
	}
	int tag = 1;
	tag <<= d - 1;
	if (!ch[p][0] || !ch[p][1]) 
	{
		if (ch[p][0]) solve(ch[p][0], d - 1, mr, w, x | tag, ans | tag);
		if (ch[p][1]) 
		{
			if (x + mr > ans) solve(ch[p][1], d - 1, mr, w, x, ans | tag);
			else 
			{
				res = max(res, x + mr + tag - 1);
				solve(ch[p][1], d - 1, mr, w, x | tag, ans);
			}
		}
		return ;
	}
	bool flag = 0;
	if (w + s[ch[p][0]] <= m) 
	{
		if (min(mr, minn[ch[p][0]]) + x > ans) solve(ch[p][1], d - 1, min(mr, minn[ch[p][0]]), w + s[ch[p][0]], x, ans | tag), flag = 1;
		else res = max(res, min(mr, minn[ch[p][0]]) + x + tag - 1);
	}
	if (w + s[ch[p][1]] <= m) 
	{
		if (min(mr, minn[ch[p][1]]) + x + tag > ans) solve(ch[p][0], d - 1, min(mr, minn[ch[p][1]]), w + s[ch[p][1]], x | tag, ans | tag), flag = 1;
		else res = max(res, min(mr, minn[ch[p][1]]) + x + tag + tag - 1);
	}
	if (!flag) 
	{
		solve(ch[p][0], d - 1, mr, w, x, ans);
		solve(ch[p][1], d - 1, mr, w, x | tag, ans);
	}
}

signed main() 
{
	int useless, T;
	long times = 0;
	useless = read();
	T = read();
	while (T--) 
	{
		times++;
		for (rint i = 0; i <= tot; i++) ch[i][0] = ch[i][1] = 0;
		res = 0;
		tot = 0;
		n = read();
		m = read();
		k = read();
		int min_ = inf;
		int sum = 0;
		for (rint i = 1; i <= n; i++) 
		{
			a[i] = read();
			min_ = min(min_, a[i]);
		}
		for (rint i = 1; i <= n; i++) 
		{
			b[i] = read();
			sum += b[i];
		}
		if (sum <= m) 
		{
			res = min_ + (1 << k) - 1;
		}
		for (rint i = 1; i <= n; i++) insert(a[i], b[i]);
		solve(0, k, inf, 0, 0, 0);
		print(res);
		cout << endl;
	}
	return 0;
}

[省选联考 2024] 迷宫守卫

题目传送门

PS:之所以直接跳到了 Day 2 T1 是因为 Day 1 T3 暴力都不会打。

题意要求满二叉树上最大化最小叶子点权字典序

显然不是 01Trie,考虑 二分和 dp 是否有操作空间

能玩,而且可做性比 Day 1 T2 高一些,剩下的泡面吃两口开始把玩

对比 Day 1 T2,思路是找当前为能否设置为 1,同样都是最大化最小什么的东西,考虑同样的思路,转化为判断能否使第一位是某个数的判定性问题

把大于等于某个数 \(x\) 的数视为 \(1\),视为 \(0\),判断是否能在魔力值够的前提下使最小字典序序列的第一位为 \(1\)。

设 \(g_i\) 为使以 \(i\) 为根的子树的最小字典序序列的第一位为 \(1\) 的最小代价,显然有转移 \(g_i=g_{i\times2}+\min(g_{i\times2+1},w_i)\)

对第一位进行操作,二分这个 \(x\),在最小字典序序列的第一位可以为 \(1\) 的前提下,\(x\) 取到最大值时,最小字典序序列的第一位一定是 \(x\)

同一个子树的最小字典序序列中的数,在最终的最小字典序序列中也一定在一起 。从 \(q_k=x\) 的结点 \(k\) 出发,一路向父亲走,走到的结点的子树所对应的最小字典序序列,在最终的最小字典序序列中排在最前面。每个走到的节点 \(u\) 的父亲的另一个儿子 \(j_u\) 的子树所对应的最小字典序序列,在最终的最小字典序序列中按照走到的顺序依次从前向后排。按照这个顺序,对于所有上述的 \(j\) 的子树,依次推倒重来,即进行上面的过程

对一些子树推倒重来时可能改变左右子树最小字典序序列的大小关系,需要考虑这些情况,保证 Bob 第一个到达的叶结点一定是 \(k\),以及不浪费魔力值。如果一个走到的节点 \(i\) 是一个右儿子,那么它的子树对应的最小字典序序列一定小于 \(j_i\) 的,因为消耗更多魔力值改变方案只会变得更大,所以可以将 \(j_i\) 的方案直接推倒重来;如果 \(i\) 是一个左儿子,那么当 \(g_{j_i}≤w_{fa_i}\)时,也可以直接将 \(j_i\) 的方案直接推倒重来;否则,如果目前方案剩余的魔力值足以从激活父亲的石像守卫改为使 \(j_i\) 的子树的最小字典序序列的第一位视为 \(1\),那么就不激活父亲的石像守卫,不然仍然需要激活;最后将 \(j_i\) 的方案推倒重来。

int check(int x, int k) 
{
	if ((x << 1) >= n)
	{
		f[x] = (a[x << 1] < k ? inf : (a[(x << 1) + 1] < k ? a[x] : 0));
	}
	else 
	{
		check(x << 1, k);
		check((x << 1) + 1, k);
		f[x] = min(inf, f[x << 1] + min(a[x], f[(x << 1) + 1]));
	}
	return f[x];
}

void dfs(int x) 
{
	int l = 1;
	int r = n;
	while (l < r) 
	{
		int mid = (l + r + 1) >> 1;
		if (check(x, mid) <= w) l = mid;
		else r = mid - 1;
	}
	check(x, l);
	l = rev[l]; w -= f[x];
	ans[++cnt] = a[l]; ans[++cnt] = a[l ^ 1];
	int i = l >> 1;
	while (i != x) 
	{
		if (i & 1) w += f[i ^ 1];
		else if (f[i ^ 1] <= a[i >> 1]) w += f[i ^ 1];
		else if (w >= f[i ^ 1] - a[i >> 1]) w += a[i >> 1];
		dfs(i ^ 1);
		i >>= 1;
	}
}

signed main() 
{
	int t;
	cin >> t;
	while (t--) 
	{
		cin >> m >> w;
		n = 1 << m;
		cnt = 0;
		for (rint i = 1; i < 2 * n; i++) cin >> a[i];
		for (rint i = n; i < 2 * n; i++) rev[a[i]] = i;
		dfs(1);
		for (rint i = 1; i <= n; i++) cout << ans[i] << " ";
	    cout << endl;
	}
	return 0;
}

[省选联考 2024] 重塑时光

传送门

对于 85pts 部分的 dp 还是比较好弄得,但是剩下优化到可以卡进去的代码我是不会写的,看别人的题解自己写还算不出来,写到 85pts 算了。

设 \(f_S\) 表示集合为 \(S\) 的点放在一段,内部排序有多少的方案数。

设 \(h_{i,S}\) 表示集合为 \(S\) 的点分成 \(i\) 段,使得这 \(i\) 段中两两没有边。dp 时枚举超集转移即可。

设 \(g_{i,S}\) 表示现在加了 \(i\) 个非空段,且它们由集合 \(S\) 里的点组成且合法的方案数。这 \(i\) 个非空段构成一个 DAG 的时候是合法的,所以我们考虑每次假如 \(j\) 个入度为 \(0\) 的非空段,让它们与前面的段连边。但我们发现这样会重复。于是考虑更改转移意义为加入 \(j\) 个入度为 \(0\) 的非空段,使得入度为 \(0\) 的至少为 \(j\) 个,容斥一下,得出转移

\(g_{i,S}=\sum_{j=1,T} (-1)^{j+1}\times g_{i-j,S-T}\times h_{j,T}\)

复杂度 \(O(3^nn^2)\)

int qpow(int a, int b = mod - 2, int p = mod) 
{
	int res = 1;
	while (b)
	{
	    if (b & 1) res = res * a % p;	
	    a = a * a % p;
		b >>= 1;
	}
	return res;
}

bool v(int s, int t) 
{
	return adj[s] & t;
}

signed main() 
{
	int n, m, k;
	cin >> n >> m >> k;

	for (rint i = 1; i <= m; i++) 
	{
		int u, v;
		cin >> u >> v;
		u--;
		v--;
		for (rint s = 0; s < (1 << n); s++)
			if (s & (1 << u))
				adj[s] |= 1 << v;
	}

	for (rint i = 0; i < 32; i++)
		for (rint j = c[i][0] = 1; j <= i; j++) 
			c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
		

    fac[0] = ifac[0] = 1;
	for (rint i = 1; i < 32; i++) ifac[i] = qpow(fac[i] = fac[i - 1] * i % mod);

	h[0] = 1;

	for (rint s = 1; s < (1 << n); s++) 
	  for (rint i = 0; i < n; i++)
		if ((s & (1 << i)) && !v(1 << i, s ^ (1 << i))) 
		  h[s] = (h[s] + h[s ^ (1 << i)]) % mod;

	g[0][0] = 1;

	for (rint i = 1; i <= n; i++)
	  for (rint s = 1; s < (1 << n); s++) 
		for (rint t = s; t; t = (t - 1) & s)
		  if (!v(t, s ^ t) && !v(s ^ t, t)) 
			g[i][s] = (g[i][s] + g[i - 1][s ^ t] * h[t]) % mod;	

	for (rint i = 1; i <= n; i++)
		for (rint s = 1; s < (1 << n); s++) 
		{
			g[i][s] = g[i][s] * ifac[i] % mod;
			if (~i & 1) g[i][s] = mod - g[i][s];
		}

	f[0][0] = 1;

	for (rint i = 1; i <= n; i++)
	  for (rint s = 1; s < (1 << n); s++) 
		for (rint t = s; t; t = (t - 1) & s)
		  if (!v(s ^ t, t)) 
			for (rint j = i; j; j--)
			  f[i][s] = (f[i][s] + f[i - j][s ^ t] * g[j][t]) % mod;

	int ans = 0;

	for (rint i = 1; i <= min(k + 1, n); i++) 
		ans = (ans + f[i][(1 << n) - 1] * fac[i] % mod * c[k + 1][i]) % mod;

	cout << ans * fac[k] % mod * ifac[n + k] % mod << endl;
	
	return 0;
}

[省选联考 2024] 最长待机

传送门

视一条长度为 \(y\) 的全是 \(1\) 的链为 \(x^y\)

结论:

  1. \(x^y = x \times (nx^{y-1})\)(\(n\) 为常数)
  2. \(x^y+x^{y-1}=(x+1) \times x^{y-1}=x^y\)
  3. \(x^{y-1}+x^y>x^y\),因为左式的 \(x^{y-1}\) 和右式的 \(x^y\) 同时输入,所以可以保证左式的 \(x^y\) 比右式大,不能忽略。

对询问的情况分类讨论:

  1. \(e_{k}=1\),所以此时的答案和 \(k\) 所在的子树中,与 \(k\) 的 \(1\) 最多的链的 \(1\) 的数量有关。
  2. \(e_{k}=0\),此时我们从 \(k\) 向下遍历直到遍历到第一个 \(1\) 或者叶子结点的 \(0\),把它的答案(根据 \(e_k = 1\) 部分的答案)从左到右排序,形成一个序列,它的答案就是不能被忽略的数的和,即那些左边没有数字比它大的数的和。

复杂度 \(O(nm)\) 可以直接写

int bfs(int x, int depth) 
{
	int res = depth;
	for (auto y : e[x]) res = max(res, bfs(y, depth + a[y]));
	return res;
}

void dfs(int x) 
{
	if (a[x]) 
	{
		v.push_back(bfs(x, 1));
		return ;
	} 
	else if (e[x].empty())
	{
		v.push_back(0);
	} 
	for (auto y : e[x]) dfs(y);
}

signed main() 
{
	cin >> n >> m;
	for (rint i = 1; i <= n; i++) 
	{
		int k;
		cin >> a[i] >> k;
		for (rint j = 1; j <= k; j++) 
		{
			int x;
			cin >> x;
			e[i].push_back(x);
		}
	}
	while (m--) 
	{
		cin >> op >> x;
		if (op == 1) 
		{
			a[x] ^= 1;
		}
		if (op == 2)  
		{
			v.clear();
			dfs(x);
			if (v.size() == 1) 
			{
				cout << v[0] + (v[0] == 0) << endl;
			}
			else 
			{
				int last = -1, ans = 0, idx = 0;
				for (auto i : v) 
				{
					if (i >= last) 
					{
						if (i == 0) ans++;
						else ans += i;
						idx++;
						last = i;
					}
				}
				cout << ans + (idx != 1) << endl;
			}
		}
	}
	return 0;
} 

之后用线段树优化不会写,可以停了。

后记

怎么说呢,现在 HE 已经是弱省了,除了 Day 1 T1 要稳过以外别的题暴力打满能拿得分都拿到就好了。现在 CCF 出题方向不再是考阴间的算法而是在于基础,这次省选考的算法其实都是 NOIP 大纲里的,如果可以把 NOIP 范围内的算法做到炉火纯青打省选其实一点问题都没有。

简言之,在保证自己能吃的泡面都吃到的基础上再去考虑能不能加餐。

标签:ch,min,省选,res,2024,int,子树,ans,联考
From: https://www.cnblogs.com/spaceswalker/p/18076677

相关文章

  • 20240315,逻辑类型,条件和逗号,函数,数组
    刚好看到逻辑类型,今天早上有个很好玩的事情,一早上醒来圆圆的小狗跑到了床下,然后她说“你是不是打我的小狗了”我;”我没有,我什么都不知道””他的屁股都扁了“我:“我怎么知道,他的屁股扁了关我什么事"“你怎么知道他的屁股扁了”我“不是你说的嘛”“我诈你的”,然后走了......
  • 【2024.03.12】定时执行专家 V7.2 发布 - TimingExecutor V7.2 Release
    目录▉软件介绍▉新版本V7.2 下载地址▉ V7.2新功能▼2024-03-12 V7.2 -更新日志▉ V7.x 新UI设计▉软件介绍《定时执行专家》是一款制作精良、功能强大、毫秒精度、专业级的定时任务执行软件。软件具有25种【任务类型】、12种【触发器】触发方式,并且......
  • 联合省选 2024 游记
    Day0写了一堆板子。但是不希望能用上。怕考场上紧张写挂。Day16:50起床。感觉进考场前有一点点困啊,不过不要紧,优势在我!8:2?开题,wind,xor,wormhole!wind看起来是一个不难的数学题,xor看上去可以拼很多暴力,wormhole应该是一个防AK的计数。正常操作,先做wind,想起去......
  • q1-投资理财-2024.3.15
    q1-投资理财-2024.3.15​ 兴趣使然,在20岁接触到了股票,虽然没怎么赚钱并且一直都在赔钱,不过在家没有别的盈利能力,股票和期货成为搞钱的内容,期货我想碰的是鸡蛋期货,一般都是12月可能有小幅度上涨,整体一直下跌到2月份,有时候234月都是下跌的,一直到5月份会到底然后上涨到7月8月份,有的......
  • 【专题】2024年中国企业3C数码商用品电商采购白皮书报告合集PDF分享(附原数据表)
    原文链接:https://tecdat.cn/?p=35374原文出处:拓端数据部落公众号近年来,企业电商采购市场呈现稳健增势,主要得益于两方面。首先,企业对采购效率和透明度的要求日益提升,推动了市场的快速发展。其次,对供应商资源整合能力和响应速度的高标准,也进一步促进了市场的繁荣。此外,随着技术的......
  • 更新用户基本信息-完成参数校验(2024-3-15)
    实体参数校验@NotNull@NotEmpty@Email接口方法的实体参数上添加@Validated注解@PutMapping("/update")publicResultupdate(@RequestBody@ValidatedUseruser){userService.update(user);returnResult.success();}@NotNullprivate......
  • 华为OD机试真题-欢乐的周末-2024年OD统一考试(C卷)
    题目描述:小华和小为是很要好的朋友,他们约定周末一起吃饭。通过手机交流,他们在地图上选择了多个聚餐地点(由于自然地形等原因,部分聚餐地点不可达),求小华和小为都能到达的聚餐地点有多少个?输入描述:第一行输入m和n,m代表地图的长度,n代表地图的宽度。第二行开始具体输入地图信息,......
  • 2024-03-15 leetcode写题记录
    目录2024-03-15leetcode写题记录32.最长有效括号题目链接题意解法42.接雨水题目链接题意解法动态规划双指针2024-03-15leetcode写题记录32.最长有效括号题目链接32.最长有效括号题意给你一个只包含$'\((\)'和'\()\)'的字符串,找出最长有效(格式正确且连续)括号子串的......
  • 日记 2024.3.15:2024 年 syzx 春季训练 1
    日记2024.3.15:2024年syzx春季训练1A找出在\(1,2\)周围一圈的点,挑出最远点\(u,v\)(找不到说明\(d_{1,2}=1\)),判一下\(d_{u,v}\)与\(d_{u,2}\)的关系以区分\(\pm1\)。这样比较好看。B普通冒泡\(n(n-1)/2\)次,这题\(n^2\),说明每做一次操作可以浪费一次操作。......
  • 一体机 配置记录2024
    用于数据采集  鲁大师详细报表软件版本鲁大师6.1024.3970.311模块版本5.1024.1705.130检测时间2024-03-1518:48:12官方网站http://www.ludashi.com概览电脑型号X64兼容台式电脑操作系统Windows10专业版64位(Version21H2/DirectX12)处理器英特尔第三代酷......