C. Have Your Cake and Eat It Too
题目:
思路:
暴力,太暴力了
简化一下题意,就是让我们在a,b,c三个数组中选3段无重复的连续区间,使得每段区间和都是sum/3,其中sum是每个数组的和,并且保证每个数组和相同
既然要选区间,那么最后肯定是如下图所示的选法
即一段左边,一段中间,一段右边
但是谁中间谁左边谁右边呢?
无所谓,暴力枚举会拯救一切,我们直接排列组合一下即可,发现最多就6种情况,所以我们可以枚举
那么如何选区间呢?我们可以用两个指针,左指针从左边开始累加,如果总和大于sum/3那就停止,右指针同理,最后 l~r 就是中间的和了,然后判断一下是否可行即可,如果可以就输出,否则看下一种情况 ,如果都不行就输出-1
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
int n;
void Print(const vector<pair<int, int>>& ans)
{cout << ans[0].first << " " << ans[0].second << " ";cout << ans[1].first << " " << ans[1].second << " ";cout << ans[2].first << " " << ans[2].second << endl;
}
bool check(const int& sum,const vector<int>& a, const vector<int>& b, const vector<int>& c,const int& o, const int& t, const int& s)
{vector<pair<int, int>> ans(3, { 0,0 });int l = 1, r = n;int sa = 0, sb = 0, sc = 0;while (sa < sum){sa += a[l++];}while (sc < sum){sc += c[r--];}for (int i = l; i <= r; i++){sb += b[i];}if (sa >= sum && sb >= sum && sc >= sum){ans[o-1] = { 1,l-1};ans[t-1] = { l,r };ans[s-1] = { r+1,n};Print(ans);return true;}return false;
}void solve()
{cin >> n;vector<int> a(n+1), b(n+1), c(n+1);int sum = 0;for (int i = 1; i <= n; i++){cin >> a[i];sum += a[i];}for (int i = 1; i <= n; i++){cin >> b[i];}for (int i = 1; i <= n; i++){cin >> c[i];}sum = (sum + 2) / 3;if (check(sum, a, b, c, 1, 2, 3))return;if (check(sum, a, c, b, 1, 3, 2))return;if (check(sum, b, a, c, 2, 1, 3))return;if (check(sum, b, c, a, 2, 3, 1))return;if (check(sum, c, b, a, 3, 2, 1))return;if (check(sum, c, a, b, 3, 1, 2))return;cout << -1 << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}
拓扑排序
题目:
思路:
题目让我们将无向边变为有向边,使得最后的图无环,那么一个显然的做法就是先看一开始是否有环,如果无的话那么一定可以,否则不行
那么我们分步来写,我们首先要判断是否是一个 有向无环图,这里我们可以使用拓扑排序
拓扑排序的步骤:找到入度为0的点,将其假如队列,然后从队列中取出点,并将其子节点的入读-1,如果其子节点减完后入读也是0,那么就将其加入队列,如此往复,如果最后能把所有点都找过一遍,说明无环,否则有环
那么判断完后我们就得到拓扑排序了,这时我们就可以将无向边按照拓扑排序的优先级直接判断了,具体操作是,对于 u v 两点,如果拓扑排序中 u 在 v 前面,那就使 u -> v,否则就是 v -> u,这样我们按照拓扑排序的顺序操作到最后一定不会有环
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"void solve()
{int n, m1, m2;cin >> n >> m1 >> m2;vector<pair<int,int>> gnone;vector<vector<int>> ghas(n+1);vector<int> d(n + 1,0);vector<int> list(n + 1, 0);for (int i = 0; i < m1; i++){int u, v;cin >> u >> v;gnone.push_back({ u,v });}for (int i = 0; i < m2; i++){int u, v;cin >> u >> v;ghas[u].push_back(v);d[v]++;}int index = -1;int cnt = 0;queue<int> q;for (int i = 1; i <= n; i++){if (!d[i])q.push(i);}while (!q.empty()){int t = q.front();list[t] = n - cnt;q.pop();cnt++;for (auto son : ghas[t]){d[son]--;if (!d[son])q.push(son);}}if (cnt < n){cout << "-1\n";return;}for (int i = 0;i < m1;i++){if (list[gnone[i].first] < list[gnone[i].second])swap(gnone[i].first, gnone[i].second);cout << gnone[i].first << " " << gnone[i].second << endl;}
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;while (t--){solve();}return 0;
}
树上贪心二分
题目:
思路:
一个很显然的二分,最大值的最小值
那我们如何check呢?我们可以考虑从叶子节点往上累加,当一个点有两颗或以上的子树时,我们就要考虑贪心了,因为我们要考虑最小值,那么对于这个节点,我们就要选其所有子树中最小的那条线段,为什么?
如果我们不选最小的,假设这个节点的父亲就是根,那么线段长就是 len + 1,而选了最短的 mi + 1,其中 len > mi,显然肯定是选短的好,因此贪心成立
注意细节方面,在dfs中我们返回的是最小值,但是我们还要时刻记录最大值,比如一个节点的子树的线段分别是 4 2,而我们的 mid 是 3,那么遇到 4 的时候就要提前结束了
具体实现看代码
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
const int INF = 1e9;
void solve()
{int n;cin >> n;vector<vector<int>> g(n + 1);for (int i = 2; i <= n; i++){int father;cin >> father;g[father].push_back(i);}auto dfs = [&](auto dfs,int fa,int mid)->int{if (!g[fa].size()) return 0;int mx = -INF;int mi = INF;for (auto x : g[fa]){int t = dfs(dfs, x, mid);if (t == INF)return INF;mx = max(mx, t+1);if (mx > mid)return INF;mi = min(mi, t + 1);}return mi;};auto check = [&](int mid) {return dfs(dfs,1,mid) != INF;};int l = 0, r = n - 1;while (l + 1 < r){int mid = l + r >> 1;if (check(mid)){r = mid;}else{l = mid;}}cout << l+1 << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}