10 月 16 日模拟赛总结
Before
预期
实际
挂分
rk2/totrk7,行。
看 T1,单调栈,不会,写暴力溜。看 T2,挺线段树的,但好像维护不了,写暴力溜。看 T3,不会怎么还不会啊,挺 K 的一个树形 DP,没拿暴力 30 分是最大的失误。然后就只有 1h 了,感觉今天要爆零啊。然后开 T4,啊啊这不板子题吗,怎么最简单啊,这么牛。你直接坐坐坐坐坐坐坐下!结果被坐下了,剩下 15min 还没调出来,我恼恼恼恼恼恼恼。然后本来想输出样例的,但不能放弃,然后我调调调调调调,然后是并查集写错了,看了半小时,哈哈。只剩 5min 了大样例还是错的,我急急急急急急急。哈哈。我当场感觉今天寄了啊,垫底了啊。哈哈哈哈哈哈哈。
结果非常炸裂,我 T4 过了。经典西西弗数据啊。而且初一组就我一人 AC 了。原来是我样例复制少了,最后 3min 惊险调出来了,哈哈哈哈哈哈哈。
然后就寄了。
T1
Description
给定一个排列,每一轮对于每个数,如果它左边相邻的数比它大则删除,求几轮后不会再删除数。
Solution
这不显然的单调栈吗我考试时怎么没想出来啊。
只要不是逆序对塞进去然后答案计数即可。
考场想法:暴力。
考场寄因:暴力。
时间复杂度
Code
// 2023/10/16 PikachuQAQ
#include <iostream>
using namespace std;
const int kMaxN = 1e6 + 7;
int n, a[kMaxN], ans;
int stk[kMaxN], top;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = n, cnt; i; i--) {
for (cnt = 0; top && stk[top] < a[i]; ) {
top--, cnt++;
}
stk[++top] = a[i], ans = max(ans, cnt);
}
cout << ans;
return 0;
}
T2
Description
给定
Solution
可以证明,按右端点排序来覆盖是最优的。
于是我们使用线段树贪心的覆盖,能塞就塞。
时间复杂度
Code
// 2023/10/16 PikachuQAQ
#include <iostream>
#include <algorithm>
using namespace std;
const int kMaxN = 1e6 + 7, kMaxM = 4e6 + 7, INF = 1e9;
struct E {
int l, r;
} a[kMaxN];
bool cmp(E x, E y) {
return x.r < y.r;
}
int n, m, ans;
int seg[kMaxM], add[kMaxM];
int M(int l, int r) { return l + r >> 1; }
int L(int p) { return p << 1; }
int R(int p) { return p << 1 | 1; }
void tag(int p, int d) { seg[p] += d, add[p] += d; }
void pushup(int p) { seg[p] = min(seg[L(p)], seg[R(p)]); }
void pushdown(int p) {
tag(L(p), add[p]), tag(R(p), add[p]);
add[p] = 0;
}
void Update(int s, int t, int l, int r, int p, int d) {
if (s <= l && r <= t) {
tag(p, d);
} else {
pushdown(p);
if (s <= M(l, r)) {
Update(s, t, l, M(l, r), L(p), d);
}
if (t >= M(l, r) + 1) {
Update(s, t, M(l, r) + 1, r, R(p), d);
}
pushup(p);
}
}
int Query(int s, int t, int l, int r, int p) {
int res = INF;
if (s <= l && r <= t) {
return seg[p];
}
pushdown(p);
if (s <= M(l, r)) {
res = min(Query(s, t, l, M(l, r), L(p)), res);
}
if (t >= M(l, r) + 1) {
res = min(res, Query(s, t, M(l, r) + 1, r, R(p)));
}
return res;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1, x; i <= n; i++) {
cin >> x;
Update(i, i, 1, n, 1, x);
}
for (int i = 1; i <= m; i++) {
cin >> a[i].l >> a[i].r;
}
sort(a + 1, a + m + 1, cmp);
for (int i = 1; i <= m; i++) {
if (Query(a[i].l, a[i].r, 1, n, 1)) {
ans++;
Update(a[i].l, a[i].r, 1, n, 1, -1);
}
}
cout << ans;
return 0;
}
T3
Description
给定一棵树,对边进行黑白染色,不能有两条黑边相邻,求最多黑边数量以及对应的方案数。
Solution
有
当这个点不连接时,能匹配的点自然是它儿子最多能选雨最多不选中的最大值,也就是:
然后我们考虑选择。对于当前的能匹配的点,我们减去重复的
然后我们统计方案数。我们只考虑转移合法的,从下往上转移即可。
令
然后就有了选择的
然后 DP 数组会炸,高精度一下,就做完了。
时间复杂度
Code
// 2023/10/16 PikachuQAQ
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
const int kMaxN = 1007, INF = 1e9;
typedef long long ll;
struct BigInt {
static const int BASE = 100000000, WIDTH = 8;
vector<ll> s;
BigInt() {
*this = 0;
}
BigInt(const int& num) {
*this = num;
}
BigInt operator = (int num) {
s.clear();
do {
s.push_back(num % BASE);
num /= BASE;
} while (num > 0);
return *this;
}
BigInt operator = (const string& str) {
s.clear();
int x, len = (str.length() - 1) / WIDTH + 1;
for (int i = 0; i < len; i++) {
int end = str.length() - i * WIDTH;
int start = max(0, end - WIDTH);
sscanf(str.substr(start, end - start).c_str(), "%lld", &x);
s.push_back(x);
}
return *this;
}
bool operator<(const BigInt& b) {
if (s.size() < b.s.size()) return true;
if (s.size() > b.s.size()) return false;
for (int i = s.size() - 1; i >= 0; i--) {
if (s[i] < b.s[i]) return true;
if (s[i] > b.s[i]) return false;
}
return false;
}
bool operator >= (const BigInt& b) {
return !(*this < b);
}
bool operator == (const BigInt& b) {
if (s.size() != b.s.size()) return 0;
for (int i = 0; i < s.size(); i++)
if (s[i] != b.s[i]) return 0;
return 1;
}
BigInt operator + (const BigInt& b) {
BigInt c;
c.s.clear();
for (int i = 0, g = 0;; i++) {
if (g == 0 && i >= s.size() && i >= b.s.size()) break;
int x = g;
if (i < s.size()) x += s[i];
if (i < b.s.size()) x += b.s[i];
c.s.push_back(x % BASE);
g = x / BASE;
}
return c;
}
BigInt operator - (const BigInt& b) {
BigInt c;
c = *this;
for (int i = 0; i < c.s.size(); i++) {
int tmp;
if (i >= b.s.size())
tmp = 0;
else
tmp = b.s[i];
if (c.s[i] < tmp) {
c.s[i + 1] -= 1;
c.s[i] += BASE;
}
c.s[i] -= tmp;
}
while (c.s.back() == 0 && c.s.size() > 1) c.s.pop_back();
return c;
}
void operator -= (const BigInt& b) {
*this = *this - b;
}
BigInt operator * (const BigInt& b) {
BigInt c;
c.s.resize(s.size() + b.s.size());
for (int i = 0; i < s.size(); i++)
for (int j = 0; j < b.s.size(); j++) c.s[i + j] += s[i] * b.s[j];
for (int i = 0; i < c.s.size() - 1; i++) {
c.s[i + 1] += c.s[i] / BASE;
c.s[i] %= BASE;
}
while (c.s.back() == 0 && c.s.size() > 1) c.s.pop_back();
return c;
}
void operator *= (const BigInt& b) {
*this = *this * b;
}
friend istream& operator >> (istream& input, BigInt& x) {
string s;
if (!(input >> s)) return input;
x = s;
return input;
}
friend ostream& operator << (ostream& output, const BigInt& x) {
output << x.s.back();
for (int i = x.s.size() - 2; i >= 0; i--) {
char buf[20];
sprintf(buf, "%08d", x.s[i]);
for (int j = 0; j < strlen(buf); j++) output << buf[j];
}
return output;
}
} mod = 2, ans;
BigInt Copy(const BigInt& b, int x) {
BigInt t;
t.s.resize(b.s.size() + x);
for (int i = 0; i < b.s.size(); i++) t.s[i + x] = b.s[i];
return t;
}
BigInt Divide(const BigInt& a, const BigInt& b, BigInt& mod) {
BigInt c;
c.s.resize(a.s.size() - b.s.size() + 1);
mod = a;
int Pow[(int)log2(BigInt::BASE) + 5];
Pow[0] = 1;
for (int i = 1; i <= log2(BigInt::BASE); i++) Pow[i] = Pow[i - 1] * 2;
for (int i = c.s.size() - 1; i >= 0; i--) {
BigInt t;
t = Copy(b, i);
for (int j = log2(BigInt::BASE); j >= 0; j--)
if (mod >= t * Pow[j]) {
c.s[i] += Pow[j];
mod -= t * Pow[j];
}
}
while (c.s.back() == 0 && c.s.size() > 1) c.s.pop_back();
return c;
}
int n;
vector<int> a[kMaxN];
BigInt g[kMaxN][2], h[kMaxN];
ll f[kMaxN][2];
void DFS(int u, int fa) {
BigInt res = 1;
f[u][0] = 0, f[u][1] = -INF;
g[u][0] = 1;
for (int v : a[u]) {
if (v ^ fa) {
DFS(v, u);
h[v] = g[v][0] * (f[v][0] >= f[v][1]) + g[v][1] * (f[v][0] <= f[v][1]);
res *= h[v];
f[u][0] += f[v][1] * (f[v][0] < f[v][1]) + f[v][0] * (f[v][0] < f[v][1] == 0);
}
}
g[u][0] = res, f[u][1] = -INF;
for (int v : a[u]) {
if (v ^ fa) {
int val = f[u][0] - max(f[v][1] - f[v][0], 0ll) + 1;
if (f[u][1] < val) {
f[u][1] = val;
g[u][1] = Divide(res, h[v], mod) * g[v][0];
} else if (f[u][1] == val) {
g[u][1] = Divide(res, h[v], mod) * g[v][0] + g[u][1];
}
}
}
}
int main() {
for (int i = 1; i <= 1005; i++) {
mod *= 2;
}
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1, u, m; i <= n; i++) {
cin >> u >> m;
for (int j = 1, v; j <= m; j++) {
cin >> v;
a[u].push_back(v);
}
}
DFS(1, 0);
ans = g[1][0] * (f[1][0] >= f[1][1]) + g[1][1] * (f[1][0] <= f[1][1]);
cout << max(f[1][0], f[1][1]) << '\n' << ans << '\n';
return 0;
}
T4
Description
给定
多组数据。
Solution
很显然,当
然后这个等于处理比较麻烦,我们想到一个点数值一样,那么大小关系也是一样的,我们可以把这些互相等于的连通块缩到一个点在和其他点连边,然后进行操作。这一部分可以用并查集处理。
首先想想 no enough
怎么处理。
我们知道,当一个点的入度为零时,我们不知道还有哪些数比他更大,那么当入度为零的点数大于一时,这几个点因为没有比他们互相关系更大的了,我们判断不了他们之间的互相关系,所以这种情况就是条件缺失,并且当出度为零的点数大于一时,我们也互相判断不了这些点的大小关系,同样条件不足。这一部分可以统计缩点后的图的点入出度。
然后想想 contradiction
,冲突怎么处理。
很显然,一个合法的大小关系一定是一个 DAG,不可能出现以下情况:
A > B and B > A
A > B and B > C and C > A
也就是说,不可能图中会有环。一旦有环就会冲突。这一部分可以拓扑排序处理。注意是对缩点后的图进行。
最后,以上两种情况都不是,那么就是 yes
了。
考场想法:并查集拓扑排序。
考场寄因:没寄。
时间复杂度
Code
// 2023/10/16 PikachuQAQ
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int kMaxN = 10007;
int n, m, cnt1, cnt2, tot;
vector<int> g[kMaxN], a[kMaxN];
int in[kMaxN], out[kMaxN];
int fa[kMaxN], siz[kMaxN];
char laugh_my_axx_off;
bool ne, co;
queue<int> q;
int findfa(int x) {
return (fa[x] == x) ? x : (fa[x] = findfa(fa[x]));
}
void uni(int x, int y) {
int u = findfa(x), v = findfa(y);
if (siz[u] > siz[v]) {
swap(u, v);
}
fa[u] = v, siz[v] += siz[u];
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
while (cin >> n >> m) {
for (int i = 0; i <= n; i++) {
fa[i] = i, siz[i] = 1;
}
for (int i = 0, u, v; i < m; i++) {
cin >> u >> laugh_my_axx_off >> v;
if (laugh_my_axx_off == '<') {
g[v].push_back(u);
}
if (laugh_my_axx_off == '>') {
g[u].push_back(v);
}
if (laugh_my_axx_off == '=') {
uni(u, v);
}
}
for (int i = 0; i < n; i++) {
for (int j : g[i]) {
a[findfa(i)].push_back(findfa(j));
in[findfa(j)]++, out[findfa(i)]++;
}
}
for (int i = 0; i < n; i++) {
cnt1 += (in[i] == 0 && fa[i] == i);
cnt2 += (out[i] == 0 && fa[i] == i);
}
ne = (cnt1 > 1 || cnt2 > 1);
for (int i = 0; i < n; i++) {
if (in[i] == 0 && fa[i] == i) {
q.push(i);
}
}
while (q.size()) {
int u = q.front();
q.pop();
for (int v : a[u]) {
--in[v];
if (in[v] == 0) {
q.push(v);
}
}
}
for (int i = 0; i < n; i++) {
if (in[i] > 0 && fa[i] == i) {
co = 1;
}
}
if (co) {
cout << "contradiction\n";
} else if (ne) {
cout << "not enough\n";
} else {
cout << "yes\n";
}
co = ne = cnt1 = cnt2 = 0;
for (int i = 0; i < n; i++) {
in[i] = out[i] = 0;
g[i].clear(), a[i].clear();
}
}
return 0;
}
Summary
需要掌握的:规划时间。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现