Codeforces Round 888 (Div. 3)
D: 参考题解(https://zhuanlan.zhihu.com/p/646013267)
题意:给你一个n的全排列的全缀和数组,但是会漏掉一个 => 就只给你 n - 1 个前缀和数组问你能不能找到n的一个全排列使得它的前缀和数组满足题目要求。
思路:因为给你的是前缀和数组,所以相邻两项作差得到的数字有两种可能:
1. x 是全排列中的一个数字
2. x 是全排列中两个数字的和
因为只删掉了前缀和数组中的一个,所以情况二最多只会出现一次,那要如何判断情况二是否出现了呢?如果当前的 x 大于 n 或者已经在前面出现过了那就说明情况二出现了。
void solve()
{
int n; cin >> n;
std::vector s(n + 1, 0);
for ( int i = 1; i <= n - 1; i++)
cin >> s[i];
std::vector a;
for ( int i = 1; i <= n - 1; i++)
{
LL x = s[i] - s[i - 1];
if (x >= 1 && x <= n && !st[x]) st[x] = true ;
else a.push_back(x); // 情况二
}
if (a.size() >= 2) cout << "No" << endl;
else if (a.size() == 0) cout << "Yes" << endl;
else // 看看剩下的两个数能不能拼成
{
LL sum = 0;
for ( int i = 1; i <= n; i++)
if (!st[i]) sum += i;
if (sum == a[0]) cout << "Yes" << endl;
else cout << "No" << endl;
}
memset (st, 0, sizeof (st));
}
E:参考代码(applese大佬)(https://codeforces.com/contest/1851/standings)
题意:现有 n 种药水,你有其中的 k 种,每一种药水的价格为c[i], 现在给你每种药水的合成路径,问如果你想获得 1-n 的所有药水那么每种药水的最少花费是多少? 保证药水不能由自身合成获得
思路:可以将药水之间的合成关系建图,然后按照拓扑排序的方式顺次处理每一种药水,为什么按拓扑序枚举,因为一个点如果已经求出了答案,那么它的入度就为零
void solve() { int n, k; cin >> n >> k; std::vector<int> in(n + 1, 0); // 入度 std::vector<int> c(n + 1); // 每一种药的花费 std::vector<LL> ans(n + 1, 1e18); // 答案 std::vector<int> vis(n + 1); // 哪些药已经有了 std::vector<std::vector<int>> g(n + 1); // 存图 std::vector<LL> sum(n + 1, 0); // 合成该药的花费 for (int i = 1; i <= n; i++) cin >> c[i]; for (int i = 0; i < k; i++) { int x; cin >> x; vis[x] = 1; } for (int i = 1; i <= n; i++) { int m; cin >> m; for (int j = 0; j < m; j++) { int x; cin >> x; g[x].push_back(i); in[i]++; } } queue<int> q; for (int i = 1; i <= n; i++) if (in[i] == 0) q.push(i), sum[i] = 1e18; while (!q.empty()) { int t = q.front(); q.pop(); ans[t] = min(sum[t], (LL)(vis[t] ? 0 : c[t])); for (auto x : g[t]) { sum[x] += ans[t]; in[x]--; if (in[x] == 0) { q.push(x); } } } for (int i = 1; i <= n; i++) cout << ans[i] << ' '; cout << endl; }
F: 参考链接(https://www.cnblogs.com/bible-/p/17581785.html)
题意:
先给你n个正整数的序列a,然后再给一个k, 让你在a中找两个整数ai, aj, 在 [0, 2^k) 中找一个数x,使得(ai ^ x) & (aj ^ x)尽可能的小,求最小异或对可以用trie树,
思路:
对于ai和aj都要异或x,对于二进制第k位,只有当ai和aj的第k位相等,且与x的第k位不同,才会对ans的第k位贡献1;
要让ai和aj的每一位尽可能都相等,就要让ai异或aj最小,找出最小的异或对:n个数的序列中的最小异或对为序列排序后最小的相邻异或和
找到ai和aj后,枚举x的每一位,若ai和aj当前位一样都为0,那么x的当前位为1;若ai和aj当前位一样都为1,那么x的当前位为0
void insert(int x) { int p = 0; for (int i = k - 1; i >= 0; i--) { int u = x >> i & 1; if (!trie[p][u]) trie[p][u] = ++idx; p = trie[p][u]; } } int query(int x) { int p = 0, ans = 0; for (int i = k - 1; i >= 0; i--) { int u = x >> i & 1; if (trie[p][u] != 0) { p = trie[p][u]; ans = ans * 2 + u; } else { p = trie[p][!u]; ans = ans * 2 + !u; } } return ans; } void solve() { cin >> n >> k; int t1 = 0, t2 = 0, a1 = 0, a2 = 0; LL minn = 1e18; map<int, std::vector<int>> c; for (int i = 1; i <= n; i++) { int x; cin >> x; c[x].push_back(i); if (i != 1) { int t = query(x); if ((x ^ t) < minn) { a1 = x; a2 = t; t1 = i; for (auto x : c[t]) { if (x != i) { t2 = x; break; } } minn = x ^ t; } } insert(x); } int ans = 0; for (int i = k - 1; i >= 0; i--) { if ((a1 >> i & 1) == (a2 >> i & 1) && (a1 >> i & 1) == 0) ans = ans * 2 + 1; else ans = ans * 2 + 0; } cout << t1 << ' ' << t2 << ' ' << ans << endl; idx = 0; for (int i = 0; i <= n * k; i++) { trie[i][1] = trie[i][0] = 0; } }
G: 参考代码jiangly(https://codeforces.com/contest/1851/standings)
题意: 给你 n 座山的高度h1 - hn, 以及 m 条路,每条路连着两座山,从 hi 到 hj 的能量消耗定义为 hj - hi, 如果cost为负数则不能翻阅这座山。现在给你 q 个询问,每个询问包含三个整数u ,v, e。u为起点,v为终点,e为初始能量(可补充),问是否存在一条路径使得你可以从 u 走到 v?
思路:对于每条路来说,这条路能不能走取决于我的能量是不是大于 max(hi, hj),因为我得先爬上 hi 才能通过这条路爬到 hj. 所以边权可以定义为 max(hi, hj). 所以 u 能不能到 v 关键取决于两点:
1. u 到 v 必须是连联通
2. hu + e >= 路径上最高的一座山,因为走低山,h变小但e变大总量不变
由 1 容易想到并查集,因为查询是独立的,每一次查询时间复杂度为O(qmlog(n)),所以要离线先把所用查询先处理好, 先把所有边按边权从小到大排序(避免多次查询),然后把所有查询按照初始能量从小到大排序(避免前面有个很大的能量查询,把所有点都联通了造成误判),那样遍历所有查询的时候就把所有符合2的点放入一个连通块
#include <bits/stdc++.h>
using namespace std;
#define more ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
const int N = 2e5 + 5, MOD = 1e9 + 7;
const long long INF = 1e18;
typedef long long LL;
typedef unsigned long long usLL;
typedef pair<int, int> PII;
struct DSU
{
std::vector<int> f, siz;
DSU(){}
DSU(int n)
{
init(n);
}
void init(int n)
{
f.resize(n);
iota(f.begin(), f.end(), 0); // 从0开始自增赋值
siz.assign(n, 1);
}
int find(int x)
{
while (x != f[x])
{
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y)
{
return find(x) == find(y);
}
bool merge(int x, int y)
{
x = find(x); y = find(y);
if (x == y)
return false;
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x)
{
return siz[find(x)];
}
};
void solve()
{
int n, m; cin >> n >> m;
vector<int> h(n + 1);
for (int i = 1; i <= n; i++) cin >> h[i];
vector<tuple<int, int, int>> edges(m + 1);
for (int i = 1; i <= m; i++)
{
int u, v; cin >> u >> v;
edges[i] = {max(h[u], h[v]), u, v}; // 存下边的大小,起点, 终点
}
sort(edges.begin(), edges.end());
int q; cin >> q;
vector<tuple<int, int, int, int>> qry(q + 1);
for (int i = 1; i <= q; i++)
{
int a, b, e; cin >> a >> b >> e;
qry[i] = {h[a] + e, a, b, i};
}
sort(qry.begin(), qry.end()); // 保证枚举每个查询的时候,当前的连通块都是你可以到的点
DSU dsu(n + 1);
int j = 1;
vector<bool> ans(q + 1);
for (auto [w, u, v, i] : qry)
{
while (j <= m && get<0>(edges[j]) <= w) // 对于当前查询的能量值来说,把所有能到的点放入一个连通块
{
dsu.merge(get<1>(edges[j]), get<2>(edges[j]));
j++;
}
ans[i] = dsu.same(u, v); // 最后查一下,起点和终点是否联通
}
for (int i = 1; i <= q; i++)
{
if (ans[i]) cout << "Yes" << endl;
else cout << "No" << endl;
}
cout << "\n";
}
int main()
{
more;
int T;
cin >> T;
while (T--)
{
solve();
}
//solve();
return 0;
}

浙公网安备 33010602011771号