2020杭电多校第二场题解
Posted st1vdy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020杭电多校第二场题解相关的知识,希望对你有一定的参考价值。
2020 Multi-University Training Contest 2
施工中。。。
1001 Total Eclipse
并查集。由于每次选择最大的连通块,所以连通块每次选择最小的点,删除后选择新的连通块组继续操作。
对于每个连通块,用并查集反向处理连通块即可。
- 将当前最大的点加入图,并删除该点,连通块数加一
- 遍历该点的边,每与一个不同的集合合并,连通块数减一
- 每次贡献为连通块数 * (该点的权值 - 下一个点的权值)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int maxn = 1e5 + 5;
int b[maxn], f[maxn];
bool vis[maxn];
vector<int> E[maxn];
vector<int> V[maxn];
int main() {
int t;
scanf("%d", &t);
function<int(int)> find;
find = [&](int x)->int {
while (x != f[x])
x = f[x] = f[f[x]];
return x;
};
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
E[i].resize(0);
V[i].resize(0);
vis[i] = false;
f[i] = 0;
}
int u, v;
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u, &v);
E[u].push_back(v);
E[v].push_back(u);
}
int k = 0;
function<void(int)> dfs;
dfs = [&](int now) {
vis[now] = true;
V[k].push_back(now);
for (auto it : E[now]) {
if (vis[it]) continue;
dfs(it);
}
};
LL ans = 0;
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
k++;
dfs(i);
V[k].push_back(0);
sort(V[k].begin(), V[k].end(), [&](const int o1, const int o2) {
return b[o1] > b[o2];
});
LL cnt = 0;
for (int j = 0; j < (int)V[k].size();) {
if (!V[k][j]) break;
int p = j;
while (++j < (int)V[k].size() && V[k][j] == V[k][j - 1])
;
for (int g = p; g < j; g++) {
int now = V[k][g];
f[now] = now;
cnt++;
for (auto it : E[now]) {
if (!f[it])
continue;
if (find(now) != find(it))
f[find(now)] = find(it),
cnt--;
}
}
ans = ans + cnt * (b[V[k][p]] - b[V[k][j]]);
}
}
printf("%lld
", ans);
}
return 0;
}
1006 The Oculus
预处理出 (fib[i]\%mod) 的值,且令 (fib[i]\%mod) 的值不同
已知斐波那契数为 ([1,2e6+5]),所以可以预处理 (mod) 是否每个斐波数的模数不同
根据斐波那契数编码,将 (a、b、c) 求余 (mod) 的值计算出来出来
由于 (fib[i]\%mod) 的值各不相同,则存在唯一值使得 ((c+fib[i])\%mod=a*b\%mod)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 3799912185593857;
const int maxn = 2e6 + 5;
LL fib[maxn];
int a[maxn];
int main() {
fib[0] = fib[1] = 1;
for (int i = 2; i < maxn; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
if (fib[i] >= mod) fib[i] -= mod;
}
int t;
scanf("%d", &t);
while (t--) {
int n, x, p, q;
scanf("%d", &p);
LL a = 0;
for (int i = 1; i <= p; i++) {
scanf("%d", &x);
if (x) {
a = a + fib[i];
if (a >= mod) a -= mod;
}
}
scanf("%d", &q);
LL b = 0;
for (int i = 1; i <= q; i++) {
scanf("%d", &x);
if (x) {
b = b + fib[i];
if (b >= mod) b -= mod;
}
}
scanf("%d", &n);
LL c = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
if (x) {
c = c + fib[i];
if (c >= mod) c -= mod;
}
}
LL ans = __int128(a) * b % mod;
for (int i = 1; i <= max(n, p + q + 1); i++) {
LL temp = c + fib[i];
if (temp >= mod) temp -= mod;
if (temp == ans) {
printf("%d
", i);
break;
}
}
}
return 0;
}
1009 It‘s All Squares
本题实质是一个算复杂度的问题 。显然最大图形为 (400*400),有 (4e6/4/400=2500) 个图形, (400*400*2500=4e8) 换言之,矩形每个可以用 (O(n*m)) 复杂度完成。
对于每个多边形处理出 (x_{min},x_{max},y_{min},y_{max}) ,然后每行处理出所有竖的边界,在两个相邻边界内的就是在多边形内
(对边界排序可以用基数,但是 (sort) 也可以 (qwq))。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 405;
const int inf = 0X3f3f3f3f;
int w[maxn][maxn];
char s[maxn * maxn];
int line[maxn][maxn], top[maxn];
bool a[maxn * maxn];
int cnt[maxn * maxn];
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &w[i][j]);
for (int l = 1; l <= q; l++) {
int x, y;
scanf("%d%d", &x, &y);
scanf("%s", s + 1);
int len = strlen(s + 1);
int min_x = x, max_x = x, min_y = y, max_y = y;
for (int i = 1; i <= len; i++) {
if (s[i] == ‘L‘)
x--;
else if (s[i] == ‘R‘)
x++;
else if (s[i] == ‘D‘)
y--;
else
y++;
min_x = min(min_x, x);
max_x = max(max_x, x);
min_y = min(min_y, y);
max_y = max(max_y, y);
if (s[i] == ‘L‘)
line[x + 1][++top[x + 1]] = y;
else if (s[i] == ‘R‘)
line[x][++top[x]] = y;
}
int g = 0;
for (int i = min_x + 1; i <= max_x; i++) {
if (!top[i])
continue;
sort(line[i] + 1, line[i] + top[i] + 1);
for (int j = 1; j <= top[i]; j += 2) {
for (int k = line[i][j] + 1; k <= line[i][j + 1]; k++) {
if (!a[w[i][k]]) {
a[w[i][k]] = true;
cnt[++g] = w[i][k];
}
}
}
top[i] = 0;
}
for (int i = 1; i <= g; i++)
a[cnt[i]] = false;
printf("%d
", g);
}
}
return 0;
}
1010 Lead of Wisdom
(n) 个装备 (k) 种 ((n,kleq 50)) ,所以装备方案最多为 (3^{16}*2),所以直接爆搜+剪枝。
剪枝可以剪下界,(事实上,不剪也可以过)用每种装备最大的 (a,b,c,d) 代表每种装备的最优取值,用后缀表示最优装备的后缀,在搜索过程中若采用最优后缀仍无法超过已知最优解,则可以剪枝。
#include<bits/stdc++.h>
#define ll long long
#define maxn 100010
using namespace std;
ll ma[100][5];
ll sum[100][5];
ll val[100][5];
ll ans = 0;
int n, k;
vector<int>b[100];
void sol(int x, ll a1, ll a2, ll a3, ll a4) {
ans = max(ans, (a1 + 100) * (a2 + 100) * (a3 + 100) * (a4 + 100));
if (x == k) return;
if (ans >= (a1 + sum[x][0] + 100) * (a2 + sum[x][1] + 100) * (a3 + sum[x][2] + 100) * (a4 + sum[x][3] + 100)) return;
for (int i = 0; i < b[x + 1].size(); i++) {
int j = b[x + 1][i];
sol(x + 1, a1 + val[j][0], a2 + val[j][1], a3 + val[j][2], a4 + val[j][3]);
}
sol(x + 1, a1, a2, a3, a4);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &k);
memset(sum, 0, sizeof(sum));
memset(val, 0, sizeof(val));
memset(ma, 0, sizeof(ma));
for (int i = 0; i <= k; i++) b[i].clear();
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
for (int j = 0; j < 4; j++) {
scanf("%lld", &val[i][j]);
ma[x][j] = max(ma[x][j], val[i][j]);
}
b[x].push_back(i);
}
for (int i = k - 1; i >= 0; i--)
for (int j = 0; j < 4; j++)
sum[i][j] = sum[i + 1][j] + ma[i + 1][j];
ans = 0;
sol(0, 0, 0, 0, 0);
printf("%lld
", ans);
}
return 0;
}
1012 String Distance
由于 (m) 串只有 (20) ,首先预处理 (n) 串每个字符后面第 (i) 个字符的位置。
对于每次询问搜索 (LCS) ,用 (dp[i][j]) 表示 (LCS) 第 (i) 位是 (m) 串第 (j) 位时,对应字符在 (n) 串的位置,若在([left,right]) 范围内,即合理。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0X3f3f3f3f;
char s[maxn], p[30];
int w[maxn][26];
int dp[21][21];
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%s%s", s + 1, p + 1);
int n = strlen(s + 1);
int m = strlen(p + 1);
for (int i = 0; i < 26; i++)
w[n][i] = inf;
for (int i = n - 1; i >= 0; i--) {
for (int j = 0; j < 26; j++)
w[i][j] = w[i + 1][j];
w[i][s[i + 1] - ‘a‘] = i + 1;
}
int q;
scanf("%d", &q);
while (q--) {
int left, right;
scanf("%d%d", &left, &right);
int ans = 0;
memset(dp, 0X3f, sizeof(dp));
for (int i = 1; i <= m; i++)
dp[1][i] = w[left - 1][p[i] - ‘a‘];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= m; j++) {
if (dp[i][j] <= right) {
ans = max(ans, i);
for (int k = j + 1; k <= m; k++)
dp[i + 1][k] = min(dp[i + 1][k], w[dp[i][j]][p[k] - ‘a‘]);
}
}
}
ans = right - left + 1 - ans + m - ans;
printf("%d
", ans);
}
}
return 0;
}
以上是关于2020杭电多校第二场题解的主要内容,如果未能解决你的问题,请参考以下文章
[2020杭电多校第二场]1005 New Equipments(费用流)
2022杭电多校第二场 A.Static Query on Tree(树剖)