Codeforces Round #740 (Div. 1, based on VK Cup 2021 - Final (Engine))

A

直接按题意暴力模拟即可。时间复杂度为 \(O(t n^2)\)

代码:

#include <iostream>

using namespace std;

int a[1007];

inline bool check(int n){
	for (int i = 1; i <= n; i++){
		if (a[i] < a[i - 1]) return false;
	}
	return true;
}

int main(){
	int t;
	cin >> t;
	for (int i = 1; i <= t; i++){
		int n, ans = 0;
		cin >> n;
		for (int j = 1; j <= n; j++){
			cin >> a[j];
		}
		for (int j = 1; j <= n; j++){
			if (check(n)) break;
			for (int k = j % 2 == 0 ? 2 : 1; k < n; k += 2){
				if (a[k] > a[k + 1]) swap(a[k], a[k + 1]);
			}
			ans++;
		}
		cout << ans << endl;
	}
	return 0;
}

B

\(sum = a + b\),Alice break 了 \(u\) 次,Borys break 了 \(v\) 次。

如果 \(0 \leq u \leq \min(a, \lfloor \frac{sum}{2} \rfloor)\)\(\lceil \frac{sum}{2} \rceil \geq a - u\),则 \(k = a + \lfloor \frac{sum}{2} \rfloor - 2u\) 成立。

同理:如果 \(0 \leq v \leq \min(b, \lfloor \frac{sum}{2} \rfloor)\)\(\lceil \frac{sum}{2} \rceil \geq b - v\),则 \(k = b + \lfloor \frac{sum}{2} \rfloor - 2v\) 成立。

直接枚举 \(u, v\) 即可。时间复杂度为 \(O(t(a + b))\)

代码:

#include <stdio.h>

bool vis[200007];

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

int main(){
	int t;
	scanf("%d", &t);
	for (int i = 1; i <= t; i++){
		int a, b, sum, x, y, p, q, ansa = 0;
		scanf("%d %d", &a, &b);
		sum = a + b;
		x = sum / 2;
		y = (sum + 1) / 2;
		p = min(a, x);
		for (int j = 0; j <= sum; j++){
			vis[j] = false;
		}
		for (int j = 0; j <= p; j++){
			if (y >= a - j) vis[a + x - j * 2] = true;
		}
		q = min(b, x);
		for (int j = 0; j <= q; j++){
			if (y >= b - j) vis[b + x - j * 2] = true;
		}
		for (int j = 0; j <= sum; j++){
			if (vis[j]) ansa++;
		}
		printf("%d\n", ansa);
		for (int j = 0; j <= sum; j++){
			if (vis[j]) printf("%d ", j);
		}
		printf("\n");
	}
	return 0;
}

C

首先二分求出打完每个洞穴里的怪兽所需的最小血量。显然,先打所需血量较小的洞穴一定更优。

二分答案并在 check 函数中模拟打怪过程即可。时间复杂度为 \(O(t \log \max a_{i, j} \sum k)\)

代码:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

typedef struct {
	vector<int> v;
} Node;

int k[100007];
Node b[100007];

bool operator <(const Node a, const Node b){
	return a.v[0] < b.v[0];
}

inline bool check_each(int n, int m, ll k){
	int size = b[m].v.size();
	for (int i = 1; i < size; i++){
		if (k <= b[m].v[i]) return false;
		k++;
	}
	return true;
}

inline bool check(int n, ll k){
	ll cur = k;
	for (int i = 1; i <= n; i++){
		int size = b[i].v.size();
		for (int j = 1; j < size; j++){
			if (cur <= b[i].v[j]) return false;
			cur++;
		}
	}
	return true;
}

int main(){
	int t;
	cin >> t;
	for (int i = 1; i <= t; i++){
		int n;
		ll l = 1, r = 1e9 + 1, ans;
		cin >> n;
		for (int j = 1; j <= n; j++){
			ll LL = 1, rr = 1e9 + 1;
			cin >> k[j];
			b[j].v.clear();
			b[j].v.push_back(0);
			for (int x = 1; x <= k[j]; x++){
				int a;
				cin >> a;
				b[j].v.push_back(a);
			}
			while (LL <= rr){
				ll mid = (LL + rr) >> 1;
				if (check_each(n, j, mid)){
					rr = mid - 1;
					b[j].v[0] = mid;
				} else {
					LL = mid + 1;
				}
			}
		}
		sort(b + 1, b + n + 1);
		while (l <= r){
			ll mid = (l + r) >> 1;
			if (check(n, mid)){
				r = mid - 1;
				ans = mid;
			} else {
				l = mid + 1;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

D1

前置芝士:数论分块

显然可以 dp。

\(dp_{i, j}\) 表示从 \(i\) 开始,这次采用第 \(j\) 种运算(\(j = 1\) 表示减法,\(j = 2\) 表示除法),显然可以得到转移方程:

  1. \(dp_{i, 1} = \displaystyle\sum_{j = 1}^{i - 1} (dp_{j, 1} + dp_{j, 2})\)

  2. \(dp_{i, 2} = \displaystyle\sum_{j = 2}^i (dp_{\lfloor \frac{i}{j} \rfloor, 1} + dp_{\lfloor \frac{i}{j} \rfloor, 2})\)

答案:\(dp_{n, 1} + dp_{n, 2}\)

用数论分块优化 \(dp_{i, 2}\) 的计算即可。时间复杂度为 \(O(n \sqrt{n})\)

注意涉及 \(dp_{1, i}\) 的情况的处理。

代码:

#include <stdio.h>

typedef long long ll;

ll dp[200007][7], sum[200007][7];

int main(){
	int n, m;
	scanf("%d %d", &n, &m);
	for (register int i = 2; i <= n; i++){
		int half_i = i / 2;
		dp[i][1] = (sum[i - 1][1] + sum[i - 1][2] + 1) % m;
		dp[i][2] = i - half_i;
		for (register int j = 2, k; j <= half_i; j = k + 1){
			int ti = i / j;
			k = i / ti;
			dp[i][2] = (dp[i][2] + (dp[ti][1] + dp[ti][2]) * (k - j + 1) % m) % m;
		}
		sum[i][1] = (sum[i - 1][1] + dp[i][1]) % m;
		sum[i][2] = (sum[i - 1][2] + dp[i][2]) % m;
	}
	printf("%lld", (dp[n][1] + dp[n][2]) % m);
	return 0;
}

D2

考虑对 \(dp_{i, 2}\) 差分。

由于 \(\lfloor \frac{n}{m} \rfloor - \lfloor \frac{n - 1}{m} \rfloor = [m \mid n]\) 对于任意正整数 \(n, m\) 都成立,考虑在算出当前 \(i\)\(dp_{i, j}\) 后向它的倍数累加贡献。

时间复杂度为 \(O(n \ln n)\)

同样需要注意涉及 \(dp_{1, i}\) 的情况的处理。

代码:

#include <stdio.h>

int dp[4000001][2], sum[4000001][2];

int main(){
	int n, m;
	scanf("%d %d", &n, &m);
	for (register int i = 2; i <= n; i++){
		int t;
		dp[i][0] = (sum[i - 1][0] + sum[i - 1][1] + 1) % m;
		if (i == 2){
			dp[i][1] = 1;
		} else {
			dp[i][1] = (dp[i][1] + dp[i - 1][1] - (i % 2 == 0 ? 1 : 0) + 1) % m;
		}
		t = ((dp[i][0] + dp[i][1] - dp[i - 1][0] - dp[i - 1][1]) % m + m) % m;
		sum[i][0] = (sum[i - 1][0] + dp[i][0]) % m;
		sum[i][1] = (sum[i - 1][1] + dp[i][1]) % m;
		for (register int j = i * 2; j <= n; j += i){
			dp[j][1] = (dp[j][1] + t) % m;
		}
	}
	printf("%d", (dp[n][0] + dp[n][1]) % m);
	return 0;
}
posted @ 2021-08-25 14:58  LovelyLeasier  阅读(83)  评论(0)    收藏  举报