2022杭电多校(八)
文章目录
一、比赛小结
这场签到题挺多
二、题目分析及解法(基础题)
1001、Theramore
题目链接:Problem - 7220 (hdu.edu.cn)
题意:
一个 01 序列,可以任意翻转奇数长度的区间,求能达到的最小字典序。
题解:
考虑操作不变性,位置的奇偶性不变,按奇偶分类即可
代码:
// 1001, std
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
char s[MAXN];
int n, cnt[2][2];
void solve() {
scanf("%s", s + 1);
n = strlen(s + 1);
cnt[0][0] = cnt[0][1] = cnt[1][0] = cnt[1][1] = 0;
for (int i = 1; i <= n; ++i) {
++cnt[i & 1][s[i] == '1'];
}
for (int i = 1; i <= n; ++i) {
if (cnt[i & 1][0]) {
putchar('0');
--cnt[i & 1][0];
} else {
putchar('1');
}
}
puts("");
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
1004、Quel’Thalas
题目链接:Problem - 7223 (hdu.edu.cn)
题意:
二维平面上, [ 0 , n ] ∗ [ 0 , n ] [0, n]*[0, n] [0,n]∗[0,n] 的整点中去掉 ( 0 , 0 ) (0, 0) (0,0) 之后最少需要多少条直线使得每个点至少有一条边覆盖。
题解:
a n s = 2 ∗ n ans = 2*n ans=2∗n
我们考虑如果不切 x = n x=n x=n 和 y = n y=n y=n , 那么同一条直线最多穿越两个边界上的点,总共有 4 n 4n 4n 个边界上的点,所以至少要 2 n 2n 2n 条直线。
代码:
// 1004, std
#include <bits/stdc++.h>
using namespace std;
int main() {
// freopen("1004.in", "r", stdin);
// freopen("1004.out", "w", stdout);
int T, n;
for (cin >> T; T--;) {
scanf("%d", &n);
printf("%d\n", n * 2);
}
}
1005、Ironforge
题目链接:Problem - 7224 (hdu.edu.cn)
题意:
一条链,每个点上有一个数 a a a ,每条边上有一个质数 b i b_i bi 。一开始在某个点上,有一个空背包,走到一个点上可以把它的质因子放进背包,一条边如果背包里有那个质数就可以走。多组询问求从 x 出发能否走到 y(即求每个点能走到的最大范围)。
题解:
求出从每个点出发能到达的最大的范围,即可 O ( 1 ) O(1) O(1) 回答所有询问。
先从大到小枚举,预处理每个点只向右走的最大范围,如果能向右走一步就先取 i+1 的范围,然后沿着之前预处理的范围向后跳。向后跳的次数是均摊 O ( 1 ) O(1) O(1) 的。
然后从小到大枚举,求每个点能走到的最大范围。
如果 i i i 能走到 i + 1 i+1 i+1 :如果 i + 1 i+1 i+1 已经预处理的向右的范围无法让它走回 i i i 则取预处理的答案;否则 i + 1 i+1 i+1 和 i i i 范围相同。
如果 i i i 不能走到 i + 1 i+1 i+1 :先取预处理的范围,然后向左向右扩展,都沿着已知的范围暴力跳即可。向左向右跳的次数都是均摊 O ( 1 ) O(1) O(1) 的。
判断一个范围内是否存在某个质因子,方法有很多,标程采用的是对每个质因子维护一个 vector 存它出现的所有位置,在 vector 上二分。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn) 。
代码:
// 1005, std
#include <bits/stdc++.h>
#define mset(a, b) memset(a, b, sizeof(a))
#define mcpy(a, b) memcpy(a, b, sizeof(a))
using namespace std;
typedef long long LL;
const int MAXN = 200005;
template <typename T>
inline void read(T &WOW) {
T x = 0, flag = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') flag = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
WOW = flag * x;
}
int n, m, a[MAXN], b[MAXN], clean[MAXN], tim;
int L[MAXN], R[MAXN];
int pr[MAXN], pcnt, vis[MAXN], low[MAXN];
vector<int> pos[MAXN];
bool Check(int p, int l, int r) {
if (!pos[p].size() || pos[p].back() < l) return 0;
int x = *lower_bound(pos[p].begin(), pos[p].end(), l);
return (x <= r);
}
void sieve() {
n = 200000;
for (int i = 2; i <= n; ++i) {
if (!vis[i]) {
pr[++pcnt] = i;
low[i] = i;
}
for (int j = 1; j <= pcnt && i * pr[j] <= n; ++j) {
vis[i * pr[j]] = 1;
low[i * pr[j]] = pr[j];
if (i % pr[j] == 0) break;
}
}
}
void solve() {
read(n);
read(m);
for (int i = 1; i <= n; ++i) {
read(a[i]);
}
for (int i = 1; i < n; ++i) {
read(b[i]);
}
for (int i = 1; i <= n; ++i) {
int x = a[i];
while (x > 1) {
int nowp = low[x];
while (nowp == low[x]) {
x /= nowp;
}
if (clean[nowp] != tim) {
pos[nowp].clear();
clean[nowp] = tim;
}
pos[nowp].push_back(i);
}
}
++tim;
for (int i = n; i >= 1; --i) {
R[i] = i;
while (R[i] < n && Check(b[R[i]], i, R[i])) {
R[i] = R[R[i] + 1];
}
}
for (int i = 1; i <= n; ++i) {
if (i > 1 && R[i - 1] >= i) {
if (Check(b[i - 1], i, R[i])) {
L[i] = L[i - 1];
R[i] = R[i - 1];
} else {
L[i] = i;
}
} else {
L[i] = i;
while (1) {
bool flag = 0;
while (R[i] < n && Check(b[R[i]], L[i], R[i])) {
flag = 1;
R[i] = R[R[i] + 1];
}
while (L[i] > 1 && Check(b[L[i] - 1], L[i], R[i])) {
flag = 1;
L[i] = L[L[i] - 1];
}
if (!flag) break;
}
}
// cerr << L[i] << ' ' << R[i] << endl;
}
for (int i = 1, x, y; i <= m; ++i) {
read(x);
read(y);
if (L[x] <= y && y <= R[x]) {
puts("Yes");
} else {
puts("No");
}
}
}
int main() {
sieve();
int T;
read(T);
while (T--) {
solve();
}
return 0;
}
1007、Darnassus
题目链接:Problem - 7226 (hdu.edu.cn)
题意:
给出一个排列 p p p ,把每个位置视为点,建一个无向图, i , j i, j i,j 之间的边权为 ∣ i − j ∣ ∗ ∣ p i − p j ∣ |i-j|*|p_i-p_j| ∣i−j∣∗∣pi−pj∣ 。求这个图的最小生成树。
题解:
依次连接 i , i + 1 ( 1 ≤ i ≤ n ) i, i+1 \ (1\leq i \leq n) i,i+1 (1≤i≤n) ,这样的生成树每条边边权都 ≤ n − 1 \leq n-1 ≤n−1 ,因此存在一种最小生成树中也只有边权 n − 1 n-1 n−1 的边(Kruskal 算法的原理)。 ∣ i − j ∣ ∗ ∣ p i − p j ∣ ≤ n − 1 |i-j|*|p_i-p_j| \leq n-1 ∣i−j∣∗∣pi−pj∣≤n−1 意味着 ∣ i − j ∣ |i-j| ∣i−j∣ 和 ∣ p i − p j ∣ |p_i-p_j| ∣pi−pj∣ 必有至少一个 ≤ n − 1 \leq \sqrt{n-1} ≤n−1 ,因此可以在 O ( n n ) O(n\sqrt{n}) O(nn) 的时间复杂度内找到所有这样的边,然后使用 Kruskal 算法求出最小生成树即可。
总时间复杂度 O ( n n α ( n ) ) O(n\sqrt{n} \alpha(n)) O(nnα(n)) 。
代码:
#include <bits/stdc++.h>
#define ll long long
// #define int long long
using namespace std;
const int maxn = 5e4 + 5;
const int maxm = 1e8 + 5;
int n, sqrtn, m;
ll ans;
int p[maxn], q[maxn];
int pre[maxn];
struct e {
int u, v;
int next;
} edge[maxm];
int head[maxn];
void addedge(int u, int v, int w) {
edge[m].u = u, edge[m].v = v;
edge[m].next = head[w];
head[w] = m++;
}
void init() {
sqrtn = sqrt(n);
m = ans = 0;
memset(head, -1, sizeof(head));
for (int i = 0; i < maxn; i++) pre[i] = i;
}
int find(int x) {
if (pre[x] == x) return x;
return pre[x] = find(pre[x]);
}
void kruskal() {
int cnt = 0;
for (int i = 1; i < n; i++) {
for (int j = head[i]; j != -1; j = edge[j].next) {
int u = find(edge[j].u), v = find(edge[j].v);
if (u == v) continue;
pre[u] = v;
cnt++;
ans += i;
if (cnt == n - 1) return;
}
}
}
signed main() {
clock_t startTime = clock();
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
cin >> n;
init();
for (int i = 1; i <= n; i++) cin >> p[i], q[p[i]] = i;
for (int len = 1; len <= sqrtn; len++) {
for (int i = 1; i + len <= n; i++) {
int tmp = abs(len * (p[i] - p[i + len]));
if (tmp < n) addedge(i, i + len, tmp);
tmp = abs(len * (q[i] - q[i + len]));
if (tmp < n) addedge(q[i], q[i + len], tmp);
}
}
kruskal();
cout << ans << endl;
}
clock_t endTime = clock();
// cout << "time: " << double(endTime - startTime) / CLOCKS_PER_SEC << endl;
return 0;
}
1008、Orgrimmar
题目链接:Problem - 7227 (hdu.edu.cn)
题意:
求一棵树的最大分离集的大小
题解:
令 d p [ x ] [ 0 ] , d p [ x ] [ 1 ] , d p [ x ] [ 2 ] dp[x][0], dp[x][1], dp[x][2] dp[x][0],dp[x][1],dp[x][2] 分别表示仅考虑 x 这个子树,x 号点未选,x号点选择了但是度数为0,x 号点选了且度数为 1 的选且 x 子树内部满足
分离集定义选择点数的最大值。直接dp即可!
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
int n;
struct e {
int to, next;
} edge[maxn << 1];
int cnt, head[maxn << 1];
int dp[maxn][3];
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v) {
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void dfs(int u, int fa) {
dp[u][0] = 0; // 不匹配
dp[u][1] = 1; // 主动匹配
dp[u][2] = 1; // 被动匹配
int tmp = 0;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (v == fa) continue;
dfs(v, u);
tmp += dp[v][0];
dp[u][0] += max(dp[v][0], max(dp[v][1], dp[v][2]));
dp[u][1] = max(dp[u][1], 1 + dp[v][2] - dp[v][0]);
dp[u][2] += dp[v][0];
}
dp[u][1] += tmp;
}
int main() {
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
int size(512 << 20); // 512M
__asm__("movq %0, %%rsp\n" ::"r"((char*)malloc(size) + size));
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
init();
cin >> n;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
addedge(u, v), addedge(v, u);
}
dfs(1, 0);
int ans = 0;
ans = max(ans, dp[1][0]);
ans = max(ans, dp[1][1]);
ans = max(ans, dp[1][2]);
// for (int i = 1; i <= n; i++)
// cout << dp[i][0] << " " << dp[i][1] << " " << dp[i][2] << endl;
cout << ans << endl;
}
exit(0);
}
1011、Stormwind
题目链接:Problem - 7230 (hdu.edu.cn)
题意:
一个 n ∗ m n*m n∗m 的长方形,可以沿水平或竖直方向画若干条线,每条线的两端点都在长方形的边界上。要求这些线划分出的每个小长方形面积都 ≥ k \geq k ≥k ,求最多可以画几条线。
题解:
枚举小长方形的某个边长的最小值,由此可以求出另一个边长允许的最小值,然后求出两个方向分别最多能画几条线。时间复杂度 O ( k ) O(k) O(k) 。
代码:
#include <bits/stdc++.h>
using namespace std;
int n, m, k;
int main() {
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
cin >> n >> m >> k;
int ans = 0;
if (n < m) swap(n, m);
for (int b = 0; b < m; b++) {
int u = m / (b + 1);
int v = (k - 1) / u + 1;
int a = n / v;
ans = max(ans, a + b);
}
cout << ans << endl;
}
return 0;
}
三、题目分析及解法(进阶题)
不会做X