牛客周赛 Round 70题解

牛客周赛 Round 70题解

A 小苯晨跑

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int a[4];
    for (int i = 0; i < 4; i ++ ) cin >> a[i];
    sort(a, a + 4);
    if (a[0] == a[3]) {
        cout << "NO\n";
    } else {
        cout << "YES\n";
    }
}

int main() {
    int T; cin >> T;
    while (T -- ) solve();
    return 0;
}

B 小苯过马路

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int x, y, k, t;
    cin >> x >> y >> k >> t;
    char c; cin >> c;
    if (c == 'G') {
        if (t <= k) cout << t << endl;
        else cout << k + x + t << endl;
    } else {
        cout << k + t << endl;
    }
}

int main() {
    int T = 1;
    while (T -- ) solve();
    return 0;
}

C 小苯的字符串染色

由于可以选择长度为1的区间,所以直接把所有单个黑的区间染成白的即可。

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int n; cin >> n;
    string s; cin >> s;
    vector<int> a;
    for (int i = 0; i < n; i ++ ) {
        if (s[i] == '1') a.push_back(i + 1);
    }
    cout << a.size() << endl;
    for (auto c : a) cout << c << ' ' << c << endl;
}

int main() {
    int T = 1;
    cin >> T;
    while (T -- ) solve();
    return 0;
}

D 小苯的能量项链

题意:从左右两边开始去掉珠子,最多可以去k个,问最后剩下珠子中的左右端点权值和最大是多少。

考虑枚举最后剩下的珠子中的其中一个端点,这里以枚举右端点为例。

假如我们最后剩下的右端点是 i ,表示右边已经去掉了 ni 个珠子,那么此时左边最多只能去掉 k(ni) 个珠子,记作 d 。那么最后的左端点只可能是 [1,d+1] ,我们为了让这次枚举的左右端点和最大,需要保留左 d+1 个数的最大值,这里可以用前缀最大值来维护。

prei 表示前 i 个数的最大值,那么递推式子就是 prei=max(prei1,ai)

记得处理下 k 过大的情况即可,时间复杂度 O(n)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;

int a[N], pre[N];

void solve() {
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; i ++ ) {
        cin >> a[i];
        pre[i] = max(pre[i - 1], a[i]);
    }
    if (n == 1) {
        cout << a[1] << endl;
        return ;
    }
    int j = k + 1;
    int ans = 0;
    for (int i = n; i >= 2 && j >= 1; i -- ) {
        ans = max(ans, a[i] + pre[min(j, i - 1)]);
        j --;
    }
    cout << ans << endl;
}

int main() {
    int T = 1;
    cin >> T;
    while (T -- ) solve();
    return 0;
}

E 小苯的最短路

首先我们需要证明,在如题所示的完全图中,从 1 到任意一点 i 的最短路就是直接从 1 走到 i ,不绕路。接下来我们证明一下。

考虑反证法,假设存在一个点 i ,使得 1i 的最短路是需要绕路的,我们设绕的这个点是 j 。那么一定有 i1>1j+ji ,由于 aba+b ,所以

i1=ijj1ij+j1

这与刚刚的式子矛盾,所以最短路就是直达,不用绕路。

知道了这个结论后,我们令 disi1i 的最短路,那么当输入为 n 的时候,我们只需要求 i=1ndisi 即可,打表可以发现规律。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

void solve() {
    int n; cin >> n;
    if (n % 4 == 0) {
        cout << n << endl;
    } else if (n % 4 == 1) {
        cout << 0 << endl;
    } else if (n % 4 == 2) {
        cout << (n ^ 1) << endl;
    } else {
        cout << 1 << endl;
    }
}

int main() {
    int T = 1;
    cin >> T;
    while (T -- ) solve();
    return 0;
}

F 小苯的优雅好序列

题目需要让 a 数组中的每个连续子数组都是优雅的,容易看出其实我们只需要让所有长度为 2 的连续子数组优雅就可以了。

那么接下来思考如何根据给定的 aiaj 求出 x

我们不妨设 aiaj ,题目需要求出满足要求的 (ai+x)|(aj+x) ,即存在一个正整数 k 满足

aj+x=k(ai+x)

ajai=(k1)(ai+x)

也就是 (ai+x)|(ajai)

所以我们只要枚举 ajai 的因子即可。最后的 x 其实就是用所有相邻两数之差 aiai1 的因子求出的 x 的交集,所以我们先随便求一组差的解集,然后枚举所有的 x ,验证即可。

由于一个数 h 的因子数大概是 log 级别的,所以最后的时间复杂度为 O(nlogai)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;

int a[N], n;

bool check(int x) {
	for (int i = 2; i <= n; i ++ ) {
		int val = abs(a[i] - a[i - 1]);
		if (val % (x + min(a[i], a[i - 1])) != 0) return false;
	}
	return true;
}

void solve() {
	int k;
	cin >> n >> k;
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	int pos = -1;
	for (int i = 2; i <= n; i ++ ) {
		if (a[i] == a[i - 1]) continue;
		if (pos == -1 || abs(a[i] - a[i - 1]) < abs(a[pos] - a[pos - 1]))
			pos = i;
	}
	if (pos == -1) {
		cout << k << ' ' << 1ll * (1 + k) * k / 2 << endl;
		return ;
	} 
	int val = abs(a[pos] - a[pos - 1]);
	vector<int> v;
	for (int i = 1; i * i <= val; i ++ ) {
		if (val % i) continue;
		int v1 = i - min(a[pos], a[pos - 1]);
		int v2 = val / i - min(a[pos], a[pos - 1]);
		if (v1 > 0 && v1 <= k) v.push_back(v1);
		if (v2 > 0 && v2 <= k) v.push_back(v2);
	}
	sort(v.begin(), v.end());
	v.erase(unique(v.begin(), v.end()), v.end());
	int cnt = 0;
	LL sum = 0;
	for (int x : v) {
		// cout << x << ' ';
		if (check(x)) {
			cnt ++;
			sum += x;
		}
	}
	cout << cnt << ' ' << sum << endl;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	int T; cin >> T;
	while (T -- ) solve();
	return 0;
}

G 小苯的树上操作

容易看出是换根dp,我们可以这样定义:

  • dp1u,2 表示以 u 为根的子树,u 不去掉的最大点权和
  • dp1u,0dp1u,1 分别表示在以 u 为根的子树中,只保留一颗子树的最大权值和次大权值(不包括 u 的权值)
  • dp2u,2 表示以 u 为根,u 不去掉的最大点权和
  • dp2u,0dp2u,1 分别表示在以 u 为根时,只保留一条相连的边的最大权值和次大权值(不包括 u 的权值)

接下来考虑转移

我们设当前树的根是 1 ,遍历到 u 时,设 um 个孩子,其中 u 的第 i 个孩子为 vi

在求 dp1u,2 时,考虑从某个孩子 vi 转移过来,可以考虑保留 vi 节点,从 dp1vi,2 转移过来;也可以不保留 vi 节点,直接去掉整个子树,也就是 0 ;还可以从 vi 的最大子树转移过来(对应题目中的第二种操作),也就是 dp1vi,0 。以上这些取 max 即可。

dp1u,2=i=1mmax(dp1vi,2,dp1vi,0,0)

在求 dp1u,0dp1u,1 的时候,也和刚刚一样,用 max(dp1vi,2,dp1vi,0,0) 来更新即可。

在求 dp2u,2 的时候,设 u 的父节点是 w , 首先默认 dp2u,2=dp1u,2 ,然后考虑加上 w 相关的值。 如果带上 w 本身,那么值是 dp2w,2 ,但是这其中可能会有 u 相关的值,也就是 max(dp1u,2,dp1u,0,0) ,需要减去;如果不带上 w ,那么值就是 dp2w,0

但是这个值有可能就是 u 的子树的权值和,如果发现是的话,就要用 dp2w,1 来更新,以上取一个 max 后加上即可。

在求 dp2u,0dp2u,1 的时候,也和刚刚一样,用上面算出来的值更新即可。

时间复杂度 O(n)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;

int a[N];
LL dp1[N][3], dp2[N][3];
vector<int> g[N];

void init(int n) {
	for (int i = 1; i <= n; i ++ ) {
		g[i].clear();
		for (int j = 0; j < 3; j ++ ) 
			dp1[i][j] = dp2[i][j] = 0;
	}
}
void dfs1(int u, int last) {
	dp1[u][2] = a[u];
	for (int v : g[u]) {
		if (v == last) continue;
		dfs1(v, u);
		LL val = max(dp1[v][2], dp1[v][0]);
		if (val > 0) dp1[u][2] += val;
		if (val > dp1[u][0]) {
			dp1[u][1] = dp1[u][0];
			dp1[u][0] = val;
		} else if (dp1[v][2] > dp1[u][1]) {
			dp1[u][1] = val;
		}
	}
}
void dfs2(int u, int last) {
	for (int v : g[u]) {
		if (v == last) continue;
		dp2[v][0] = dp1[v][0];
		dp2[v][1] = dp1[v][1];
		dp2[v][2] = dp1[v][2];
		LL val1 = dp2[u][2];
		LL tmp = max(dp1[v][0], dp1[v][2]);
		if (tmp > 0) val1 -= tmp;
		LL val2 = dp2[u][0];
		if (dp2[u][0] == tmp) val2 = dp2[u][1];
		LL val = max(val1, val2);
		dp2[v][2] += val;
		if (val > dp2[v][0]) {
			dp2[v][1] = dp2[v][0];
			dp2[v][0] = val;
		} else if (val > dp2[v][1]) {
			dp2[v][1] = val;
		}
		dfs2(v, u);
	}
}

void solve() {
	int n; cin >> n;
	init(n);
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	for (int i = 1; i < n; i ++ ) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs1(1, 0);
	dp2[1][0] = dp1[1][0];
	dp2[1][1] = dp1[1][1];
	dp2[1][2] = dp1[1][2];
	dfs2(1, 0);
	LL ans = 0;
	for (int i = 1; i <= n; i ++ ) ans = max(ans, dp2[i][2]);
	cout << ans << endl;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
	int T; cin >> T;
	while (T -- ) solve();
	return 0;
}
posted @   Time_Limit_Exceeded  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示