A. Twin Permutations
题意:
给一个长度为n的排列a,求另外一个长度为n的排列b,使a[1]+b[1]<=a[2]+b[2]<=…。
思路:
让a最大对应最小,最小对于最大即可,这样所以a[i]+b[i]都是相等的。
代码:
int n, a[maxn], id1[maxn];
int b[maxn], id2[maxn];
bool cmp1(int i, int j) {
return a[i] < a[j];
}
bool cmp2(int i, int j) {
return a[i] > a[j];
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], id1[i] = id2[i] = i;
sort(id1 + 1, id1 + n + 1, cmp1);
sort(id2 + 1, id2 + n + 1, cmp2);
for (int i = 1; i <= n; i++) b[id1[i]] = a[id2[i]];
for (int i = 1; i <= n; i++) cout << b[i] << ' ';
cout << endl;
}
B. Array merging
题意:
给出两个长度为n的数组a,b,现在每次可以取出任意一个数组的第一个元素,放到c数组的后面,c数组一开始为空,求c数组连续相等的最长子串长度。
思路:
这里可以用两个map把a,b数组每个元素对应的连续相等的最长子串的长度存起来,然后找到最大值即可,具体看代码。
代码:
int n, a[maxn], b[maxn];
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
map<int, int> mp1, mp2;
for (int i = 1; i <= n; i++) {
int cnt = 1;
while (i + 1 <= n && a[i] == a[i + 1]) cnt++, i++;
mp1[a[i]] = max(mp1[a[i]], cnt);
}
for (int i = 1; i <= n; i++) {
int cnt = 1;
while (i + 1 <= n && b[i] == b[i + 1]) cnt++, i++;
mp2[b[i]] = max(mp2[b[i]], cnt);
}
int ans = 0;
for (auto p : mp1) ans = max(ans, p.second + mp2[p.first]);
for (auto p : mp2) ans = max(ans, p.second + mp1[p.first]);
cout << ans << endl;
}
C. Copil Copac Draws Trees
题意:
给一个有n个节点的数,现在建树,要求当前边的两个点必须有一个点已经绘制完成,从1号点开始。如果两个点都未绘制过,则开始一次新的操作,问建成这棵树需要的操作数。
思路:
其实就是模拟键边的顺序,先把每条边的序号记下来,然后对1号点的儿子进行dfs,如果对于每个点,如果当前点接入图的边的序号在其儿子节点之后,则操作数要加一。
代码:
int n;
int dfs(int u, int fa, vector<vector<int>>& g, map<pair<int, int>, int>& mp) {
int ans = 0;
for (auto p : g[u]) {
if (p == fa) continue;
ans = max(ans, (mp[{fa, u}] > mp[{u, p}]) + dfs(p, u, g, mp));
}
return ans;
}
void solve() {
cin >> n;
vector<vector<int>> g(n + 1, vector<int>());
map<pair<int, int>, int> mp;
for (int i = 1; i <= n - 1; i++) {
int u, v;
cin >> u >> v;
mp[{u, v}] = i;
mp[{v, u}] = i;
g[u].push_back(v);
g[v].push_back(u);
}
int ans = 0;
for (auto p : g[1]) ans = max(ans, dfs(p, 1, g, mp));
cout << ans + 1 << endl;
}
D. The BOSS Can Count Pairs
题意:
给定两个数组a和b,长度均为n。
要求找到【i,j】的对数,使得1<=i<j<=n&&a[i]*a[j]==b[i]+b[j]。
思路:
对于每个a[i]记录与它对应的b[i]。然后枚举a[i],用f数组记录每个选择的贡献,先求相同a[i]对应的b[i]的贡献,再求不同的贡献,注意每个循环最后f数组的处理。
代码:
int n, a[maxn], b[maxn], f[maxn];
vector<int> v[maxn];
void solve() {
cin >> n;
for (int i = 1; i <= n * 2; i++) v[i].clear();
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i], v[a[i]].push_back(b[i]);
int ans = 0;
for (int x = 1; x * x <= 2 * n; x++) {
for (auto j : v[x]) {
ans += x * x - j > 0 ? f[x * x - j] : 0;
f[j] ++;
}
for (int y = x + 1; x * y <= 2 * n; y++)
for(auto j : v[y])
ans += x * y - j > 0 ? f[x * y - j] : 0;
for (auto j : v[x]) f[j] --;
}
cout << ans << endl;
}
E. Hyperregular Bracket Strings
题意:
给出n和k,有k组区间l,r,要求满足每个区间都是匹配的括号序列,且自己整体也是一个匹配的括号序列,长度为n,求满足条件的括号序列的数量,答案对998244353取模。
思路:
匹配的括号序列的数量即卡特兰数,先用异或一个随机数来标记区间,然后利用异或前缀和来记录每个位置的贡献。具体看代码。
tips:
随机数获取:
mt19937_64 rnd(chrono::steady_clock::now().time_since_epoch().count());
头文件:
#include <random>
#include <chrono>
代码
mt19937_64 rnd(chrono::steady_clock::now().time_since_epoch().count());
int n, a[maxn];
int m;
int fac[maxn], infac[maxn];
int f[maxn];
void solve() {
map<int, int> mp;
cin >> n >> m;
for (int i = 1; i <= n; i++) a[i] = 0;
while (m--) {
int l, r;
int x = rnd();
cin >> l >> r;
a[l] ^= x;
a[r + 1] ^= x;
}
for (int i = 1; i <= n; i++) a[i] ^= a[i - 1], mp[a[i]] ++;
f[0] = 1; for (int i = 1; i <= n; ++i)if (i % 2 == 0)f[i] = fac[i] * infac[i / 2] % mod * infac[i / 2] % mod * qmi(i / 2 + 1, mod - 2, mod) % mod;
int ans = 1;
for (auto i : mp) ans = ans * f[i.second] % mod;
cout << ans << endl;
}