牛客 周赛106 20250904

牛客 周赛106 20250904

https://ac.nowcoder.com/acm/contest/116002

A:
题目大意:

void solve(){
	int n;
	cin >> n;
	if (n & 1) cout << "NO" << '\n';
	else cout << "YES" << '\n';
}

签到

B:
题目大意:
image-20250903193330737

void solve(){
	int n, k;
	cin >> n >> k;
	
	auto check = [](int x){
		int t = x, s = 0;
		while (t){
			s *= 10;
			s += (t % 10);
			t /= 10;
		}
		return s == x;
	};
	
	int cnt = 0;
	
	while (!check(n)){
		cnt ++;
		if (cnt > k) break;
		int t = n, s = 0;
		while (t){
			s *= 10;
			s += (t % 10);
			t /= 10;
		}
		n += s;	
	}
	
	if (cnt > k) cout << n << ' ' << -1 << '\n';
	else cout << n  << ' ' << cnt << '\n';
}

模拟即可,时间复杂度为 \(O(nk\log x)\)

C:
题目大意:
image-20250903193505856

void solve(){
	int n, l1, r1, l2, r2;
	cin >> n >> l1 >> r1 >> l2 >> r2;
	vector<int> a(n + 1);
	for (int i = 3; i <= n; i ++) cin >> a[i];
	for (int i = l1; i <= min(r1, l1 + 9); i ++){
		for (int j = l2; j <= min(r2, l2 + 9); j ++){
			a[1] = i % 10, a[2] = j % 10;
			bool f = 1;
			for (int k = 3; k <= n; k ++){
				if (a[k] != (a[k - 1] * a[k - 2]) % 10){
					f = 0;
					break;
				}
			}
			if (f){
				cout << i << ' ' << j <<'\n';
				return;
			} 
		}
	}
	cout << -1 << ' ' << -1 << '\n';
}

枚举所有个位的情况,按照字典序从小到大循环,时间复杂度为 \(O(100n)\)

D:
题目大意:

image-20250903193631589

void solve(){
	int n;
	cin >> n;
	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++) cin >> a[i];
	vector<int> xr[n + 1];
	for (int i = 1; i <= n; i ++){
		int x = a[i];
		do{
			xr[i].push_back(x);
			x ^= (x >> 1);
		}while(x != a[i]);
	}
	int ans = 0;
	for (int i = 1; i <= n/2; i ++){
		int j = n - i + 1;
		int s = 1e9;
		for (int l = 0; l < xr[i].size(); l ++)
			if (xr[i][l] == a[j]) s = min(l, (int)xr[i].size() - l);
		if (s == 1e9){
			cout << -1 << '\n';
			return;
		}
		ans += s;
	}
	cout << ans << '\n';
}

\(x\) 进行替换操作相当于把 \(x\) 和他自身右移一位的结果异或起来,这样的操作最多执行 \(\log x\) 次后 \(x\) 复原

所以计算每个元素通过给定的操作可以变成的数字,然后枚举回文的另一个位置上的元素是否也在循环中

for (int l = 0; l < xr[i].size(); l ++)
	if (xr[i][l] == a[j]) s = min(l, (int)xr[i].size() - l);

选取操作次数最少的方向累加答案,时间复杂度为 \(O(n\log x)\)

证明:

假设 \(x\) 的二进制表示为 \(a_0a_1a_2\cdots a_n\),那么进行一次操作后二进制序列会变为

\[a_0,a_0\oplus a_1,a_1\oplus a_2,\cdots ,a_{n-1}\oplus a_n \]

进行第二次操作会变为

\[a_0,a_1,a_0\oplus a_2,\cdots ,a_{n-2}\oplus a_n \]

定义 \(f_{i,j}\) 表示第 \(i\) 次操作后二进制序列的第 $j $ 个元素的值,得到递推式

\[f_{i,j} = f_{i-2^k,j-2^k} \oplus f_{i-2^k,j} \]

又因为 \(j - 2^k\) 足够小时变为负数,越界后的 \(a_{j-2^k}\) 为前导零,那么 \(f_{i,j}=f_{i-2^k,j}\) 成立

E:
题目大意:

image-20250903203819607

int D[10] = {1, 0, 0, 0, 1, 0, 1, 0, 2, 1};
LL val[25];

void init(){
	val[0] = 1;
	val[1] = 4;
	val[2] = 8;
	
	for (int i = 3;i <= 22; i ++){
		if (i & 1) val[i] = val[i - 1] + 4 * pow(10, i >> 1);
		else val[i] = val[i - 1] + 4 * pow(10, (i >> 1) - 1);
	}
}

void solve(){
	LL n, sum;
	cin >> n >> sum;
	
	if (sum < n){cout << -1 << '\n'; return;}
	
	LL avg = sum / n;
	LL base;
	for (int i = 0; i <= 20; i ++){
		if (val[i + 1] > avg){
			base = i;
			break;
		}
	}
	vector<LL> ans;
	while (sum < val[base + 1] *(n - ans.size())){
		ans.push_back(val[base]);
		sum -= val[base];
	}
	while (ans.size() < n)
		ans.push_back(val[base + 1]);
	for (auto x : ans) cout << x << ' ';
	cout << '\n';
}

在洞数为 \(i\) 的情况下,我们可以确定出一个值最小的 \(x\) 来满足洞数等于 \(i\)

\(8\) 可以提供两个洞,那么在能填 \(8\) 的情况下先填 \(8\) 一定最优,如果洞数为 \(1\),那么 \(x\) 一定为 \(4\) ,因为 \(0\) 不能在前导位上

所以我们构造的策略是:洞数为偶数,全部都填 \(8\) ,否则就先填一个 \(4\)

val[1] = 4;
val[2] = 8;
val[3] = 48;
val[4] = 88;
....

题目给定的 \(sum\)\(n\) ,计算出每个元素位置上可以填入的平均值 avg ,先填入平均值以下的洞数尽可能大的数

然后当剩余的 \(sum\) 足够填下平均值以上的数时,就填这些数进去

vector<LL> ans;
while (sum < val[base + 1] *(n - ans.size())){
	ans.push_back(val[base]);
	sum -= val[base];
}
while (ans.size() < n)
	ans.push_back(val[base + 1]);

F:
题目大意:image-20250903205257044

void solve(){
	int n;
	cin >> n;
	vector<int> a(n + 2, 0);
	for (int i = 1; i <= n; i ++) cin >> a[i];
	int ans = 0;
	stack<int> st;
	for (int i = 1; i <= n; i ++){
		int f = 0;
		while (st.size() && st.top() < a[i]){
			f = 1;
			st.pop();
		}
		if (st.size() && a[i] != st.top() && a[i] < st.top() && f) ans ++; 
		st.push(a[i]);
	}
	while (st.size()) st.pop();
	for (int i = n; i >= 1; i --){
		int cnt = 0;
		while (st.size() && st.top() < a[i]){
			cnt ++;
			st.pop();
		}
		if (st.size() && a[i] != st.top() && a[i] < st.top() && f) ans ++; 
		st.push(a[i]);
	}
	cout << ans << '\n';
}

当一段区间满足除左右端点外的元素都小于左右端点的最小值时,这个区间为一个美丽数组

分为两种情况讨论:

  • \(a_l > a_r\) ,类似单调栈的形式从左到右遍历数组,当 \(a_i\) 大于了栈顶元素时,说明 \(a_i\) 是一个可能的右端点

    依次弹出栈内小于 \(a_i\) 的元素,如果有栈不为空, \(a_l \ge a_i\) 且弹出的元素至少有 \(1\)

    if (st.size() && a[i] != st.top() && a[i] < st.top() && f)
    

    说明在区间 \([l,i]\) 的子数组是一个美丽数组

  • \(a_r > a_l\) 同理,从右向左遍历数组即可

posted @ 2025-09-15 19:56  才瓯  阅读(9)  评论(0)    收藏  举报