2024/10/2 CSP-S daimayuan模拟赛复盘

2024/10/2 CSP-S daimayuan

contest link (Day 7)

A. 序列

题面描述

给你一个序列 r1,r2,,rn,问有多少非负整数序列 x1,x2,,xn 满足:

  • 对于所有 i0xiri

  • 满足 x1|x2||xn=x1+x2++xn,左边为二进制或。

输出答案对 998244353 取模的结果。

输入 & 输出 & 样例 & 数据范围

输入第一行一个整数 n

接下来一行,一共 n 个整数 r1,r2,,rn

输出一个整数,表示答案。

对于所有数据,保证 1n16,0ri<260

5
1 2 3 4 5

思路解析

首先考虑将那个按位或的条件转换一下,可以得到条件等价于要求对于所有的 (i,j) 都有 xi&xj=0,相当于我们查看所有 x 的第 k 位,只有一个或没有 x 在当前位上为 1。有这种想法后我们就可以想数位 dp,但是在二进制数位下。我们在常规的数位 dp 下需要记录是否有顶头,这里同理,但是需要记录 n 个元素的信息,因为 n 的范围较小,用状压存储即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 18, M = 64;
const ll P = 998244353;
ll n, r[N], f[2][1 << N];
int g(ll x, int y) {
	return (x >> y) & 1; 
}
int main() {
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> r[i];
	f[63 & 1][0] = 1;
	for(int i = 62; i >= 0; i--) {
		for(int j = 0; j < (1 << n); j++) f[i & 1][j] = 0;
		for(int j = 0; j < (1 << n); j++) {
			int tmp = 0;
			for(int k = 1; k <= n; k++) {
				if(g(j, k - 1) || g(r[k], i)) tmp += (1 << (k - 1));
			}
			f[i & 1][tmp] += f[(i + 1) & 1][j];
			for(int k = 1; k <= n; k++) {
				int top = ((g(j, k - 1) || g(r[k], i)) ? 1 : 0);
				if(g(j, k - 1) || g(r[k], i)) {
					int now = ((tmp ^ (1 << (k - 1))) | ((g(j, k - 1)) << (k - 1)));
					f[i & 1][now] += f[(i + 1) & 1][j]; f[i & 1][now] %= P;
				}
			}
		}
	}
	ll ans = 0;
	for(int j = 0; j < (1 << n); j++) ans = (ans + f[0][j]) % P;
	cout << ans;
	return 0;
}

B. 合并数字

题面描述

n 个数字,a1,a2,,an。每次可以选择两个数字 x,y 删除,然后加入数字 Kxyn1 轮之后只剩下一个数字,问最后剩下的数字最大可能是多少?

你要对 q 个不同的 K 进行回答。每组询问独立,都是对于一开始的序列操作。

输入 & 输出 & 样例 & 数据范围

输入第一行两个整数 n,q

接下来一行 n 个整数 a1,a2,,an

接下来一行 q 个整数 K1,K2,,Kq,表示每次询问的值。

输出一共 q 行,每行一个整数,表示答案。

对于所有数据,保证 1n,q3×105,0Ki,ai109

思路解析

通过找规律或打表可以发现最终的答案的式子肯定是形如 (K×x)+(a1+a2++ay)(ay+1+ay+2++an),这里我们先将 a 从大到小排序。接下来我们考虑 xy 的关系,我们将我们进行的操作用树的方式表示出来就如下图,其中 ± 号就代表 K 的贡献,±a 就表示 a 对应的贡献。

可以发现元素的符号和其深度的奇偶性直接相关,于是我们把 +K 的个数记为 x0K 的个数记为 x1±a 的贡献同理,此时我们和上面的答案式子对应就有 x=x0x1,y=y0y1。接下来可以通过树的性质发现 2×x0=x1+y1,2×x1=x0+y01,这样我们根据 y0+y1=n,x0+x1=n1 解方程就能得到 y=n+1x3。现在我们已经知道了 x,y 之间的函数关系式,以及在确定 x,y 后答案的值,这样我们就可以以 yx(K×x)+(a1+a2++ay)(ay+1+ay+2++an) 建出一个函数,可以发现该函数是单峰的,于是可以使用三分解决。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10;
int n, Q, a[N], s[N];
bool cmp(int x, int y) {
	return x > y;
}
int calc(int k, int x, int y) {
	if(x < 0) return -2e9;
	return 2ll * s[x] - s[n] + k * y;
}
signed main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> Q;
	for(int i = 1; i <= n; i++) cin >> a[i];
	sort(a + 1, a + n + 1, cmp);
	for(int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
	int x = (2 * n - 1) % 3, y = (n + 1 - x) / 3;
//	cout << "  " << x << ' ' << y << ' ' << calc(3, x + 3 * 0, y - 2 * 0) << '\n';
	while(Q--) {
		int k; cin >> k;
		int l = 0, r = (n - x) / 3;
//		cout << "    " << l << ' ' << r << '\n';
		while(l <= r) {
			int ml = l + (r - l + 1) / 3, mr = ml + (r - l + 1) / 3;
			if(calc(k, x + 3 * ml, y - 2 * ml) > calc(k, x + 3 * mr, y - 2 * mr)) r = mr - 1;
			else l = ml + 1;
//			cout << "    " << l << ' ' << r << '\n';
		}
		int ans = max(max(calc(k, x + 3 * l, y - 2 * l), calc(k, x + 3 * r, y - 2 * r)), max(calc(k, x + 3 * l + 3, y - 2 * l - 2), calc(k, x + 3 * r - 3, y - 2 * r + 2)));
		cout << ans << '\n';
	}
	return 0;
}
posted @   2020luke  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示