ZR2022noip十连测day4 (未写完)
noip十连测day4T2-零(Zero)
题目描述
完全无向图是指任意一对顶点间都有边连接的简单无向图,n 个结点的完全无向图有 M=n(n−1)/2 条边。
如果一个 n 个结点的带权完全无向图,M 条边的权值分别是 [1,M] 这 M 个整数(即任意两条边权值不同,任意一个权值仅属于一条边),则称这张图为一个暗物质图。
现在给定一个 n 个结点 m 条边的带权无向连通图,边权是 [1,M] 中两两不等的整数,你需要添加 M−m 条边,使其成为一张暗物质图,且加边前后最小生成树的权值和不变。
问是否存在至少一种满足要求的加边方案。
解题思路
设只考虑边权 \(<k\) 后的子图为 \(G_k\)
- 对于一个边权 \(w<k\),如果这条边不在 \(G_k\) 中,则这条边两个端点在 \(G_k\) 必然联通
点击查看代码
#include <vector>
#include <stdio.h>
#include <string.h>
#include <algorithm>
const int N = 2e5 + 5, M = 4e5 + 5;
typedef long long LL;
int n, m, fa[N], sz[N]; LL sum, cnt;
struct Edge { int u, v; LL w; } edges[N];
std::vector<int> e[N];
int find(int x) {
if(x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1, u, v; i <= m; i ++) {
static LL w;
scanf("%d%d%lld", &u, &v, &w);
edges[i] = {u, v, w};
e[u].push_back(v), e[v].push_back(u);
}
std::sort(edges + 1, edges + m + 1, [](const Edge &a, const Edge &b) { return a.w < b.w; });
for(int i = 1; i <= n; i ++) fa[i] = i, sz[i] = 1;
for(int i = 1; i <= m; i ++) {
int u = find(edges[i].u), v = find(edges[i].v), w = edges[i].w;
if(u == v) continue;
if(w - i > sum - cnt) return puts("No"), 0;
sum += (LL)sz[u] * sz[v];
if(e[u].size() > e[v].size()) std::swap(u, v);
for(int t : e[u]) {
if(find(t) == v) cnt ++;
e[v].push_back(t);
}
fa[u] = v, sz[v] += sz[u];
}
puts("Yes");
return 0;
}
noip十连测day4T3-零二(Zero Two)
题目描述
有一个长度为 n 的序列 A,但是暗物质将 A 中元素重排得到了一个新的序列 B,规则如下:
令 B 初始为空,同时维护一个初始为空的小根堆 T,然后进行以下两类操作各 n 次:
将当前 A 的第一个元素删除并加入小根堆 T 中;
将小根堆 T 的堆顶删除并加入 B 的末尾,需要保证 T 非空。
操作的顺序是任意的,问总共可能得到多少种不同的 B,答案对 998244353 取模。
输入格式
第一行:一个整数 n,表示 A 的长度。
第二行:n 个整数 A1,…,An,描述序列 A。
输出格式
第一行:一个整数 ans,表示不同的 B 的数量对 998244353 取模的结果。
- 对于 [1,n] 中最大的数 b[p]=n
- 由堆的性质可以知道: A的前若干个数和B的前若干个数相同
- 于是被划分成两个子问题;使用区间DP解决问题
点击查看代码
#include <stdio.h>
#include <string.h>
#include <algorithm>
const int N = 105, mod = 998244353;
typedef long long LL;
int n, a[N], b[N], f[N][N][N]; // f[l][r][i] 表示 由[l,r] 中不超过i的数构成的子序列的答案
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", a + i), b[i] = a[i] * n + i;
std::sort(b + 1, b + n + 1);
for(int i = 1; i <= n; i ++) a[i] = std::lower_bound(b + 1, b + n + 1, a[i] * n + i) - b;
for(int i = 1; i <= n + 1; i ++) for(int j = 0; j <= n; j ++) f[i][i - 1][j] = 1;
for(int len = 1; len <= n; len ++)
for(int l = 1, r = len; r <= n; l ++, r ++) {
f[l][r][0] = 1;
for(int i = 1; i <= n; i ++) {
int t = -1;
for(int k = l; k <= r; k ++) if(a[k] == i) t = k;
if(~t) {
for(int k = t; k <= r; k ++)
if(a[k] <= i)
(f[l][r][i] += (LL)f[l][k][i-1] * f[k+1][r][i] % mod) %= mod;
} else f[l][r][i] = f[l][r][i - 1];
}
}
printf("%d\n", f[1][n][n]);
return 0;
}