ABC372(A-F)
A - delete .¶
题目大意
给你一个字符串 \(S\),删除其所有的 .
参考代码
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
cin >> s;
s.erase(remove(s.begin(), s.end(), '.'), s.end());
cout << s << endl;
return 0;
}
B - 3^A¶
题目大意
给你一个正整数 \(M(1 \le M \le 10 ^5)\),找到一个正整数 \(N\) 和一组序列 \(A = (A_1, A_2, \cdots, A_N)\) 满足下列所有条件:
- \(1 \le N \le 20\)
- \(0 \le A_i \le 10(1 \le i \le N)\)
- \(\sum\limits_{i = 1}^N3^{A_i} = M\)
输出任意一组解即可。
解题思路
可以枚举,也可以从最低位开始消除。
参考代码
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int m;
cin >> m;
vector<int> a;
int p = 1, i = 0;
while(3 * p < m)
{
p *= 3;
i++;
}
while(m)
{
if(m >= p)
{
a.push_back(i);
m -= p;
continue;
}
p /= 3;
i--;
}
cout << a.size() << endl;
for(auto x : a)
cout << x << ' ';
return 0;
}
C - Count ABC Again¶
题目大意
给你一个长度为 \(N(3 \le N \le 2 \times 10^5)\) 字符串 \(S\),同时给你 \(Q(1 \le Q \le 2 \times 10^5)\) 个询问,每个询问包括一个整数 \(X_i\) 和一个字符 \(C_i\),你需要将 \(S_{X_i}\) 替换成字符 \(C_i\),然后回答出 \(S\) 有多少个 ABC
子串。
解题思路
读入字符串 \(S\) 马少处理出当前有多少个 ABC
子串,记为 ans
替换前需要判断 s.substr(x-2, 3)
,s.substr(x-1, 3)
,s.substr(x, 3)
是不是 ABC
,如果是则 ans--
。
替换前需要判断 s.substr(x-2, 3)
,s.substr(x-1, 3)
,s.substr(x, 3)
是不是 ABC
,如果是则 ans++
。
参考代码
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n, q;
cin >> n >> q;
string s;
cin >> s;
int now = 0;
for (int i = 0; i < n; i++)
if (s.substr(i, 3) == "ABC")
now++;
while (q--)
{
int x;
char c;
cin >> x >> c;
x--;
if (s[x] == c)
{
cout << now << endl;
continue;
}
if (x - 2 >= 0 && s.substr(x - 2, 3) == "ABC")
now--;
if (x - 1 >= 0 && s.substr(x - 1, 3) == "ABC")
now--;
if (s.substr(x, 3) == "ABC")
now--;
s[x] = c;
if (x - 2 >= 0 && s.substr(x - 2, 3) == "ABC")
now++;
if (x - 1 >= 0 && s.substr(x - 1, 3) == "ABC")
now++;
if (s.substr(x, 3) == "ABC")
now++;
cout << now << endl;
}
return 0;
}
D - Buildings¶
题目大意
有 \(N(1 \le N \le 2 \times 10^5)\) 个建筑,第 \(i\) 个建筑的高度是 \(H_i\),对于每一个 \(i = 1, 2, \cdots, N\),找到满足下列条件的 \(j(i < j \le N)\) 的个数:
- 在第 \(i\) 个建筑和第 \(j\) 个建筑中间不存在另一个建筑比第 \(j\) 个建筑高。
解题思路
对于一组满足条件的 \((i, j)\),有 \(\max\{H_{i+1}, H_{i+2}, \cdots, H_{j-1}\} \le H_j\)。
如果固定 \(i\),求出有多少个 \(j\) 满足条件是困难的,但如果固定 \(j\),只需要知道 \(j\) 左边第一个比 \(H_j\) 大的位置,设这个位置为 \(t\),则 \(j\) 可以给 \(t\) 到 \(j-1\) 的所有位置贡献 \(1\)。所以弄一个从左到右单调递增的单调栈,用差分数组维护每个 \(j\) 的贡献即可。
参考代码
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> h(n + 1), b(n + 1);
for (int i = 1; i <= n; i++)
cin >> h[i];
h[0] = n + 1;
stack<int> st;
st.push(0);
for (int i = 1; i <= n; i++)
{
while (h[st.top()] < h[i])
st.pop();
int l = st.top();
b[l]++;
b[i]--;
st.push(i);
}
for (int i = 1; i <= n; i++)
{
b[i] += b[i - 1];
cout << b[i] << ' ';
}
return 0;
}
E - K-th Largest Connected Components¶
题目大意
给你一个 \(N(1 \le N \le 2 \times 10^5)\) 个点的无向图,初始的时候没有任何边。给你 \(Q(1 \le Q \le 2 \times 10^5)\) 个询问,询问有两种类型:
- 类型 1:
1 u v
,表示在点 \(u\) 和点 \(v\) 之间添加一条无向边。 - 类型 2:
2 v k
,表示询问和点 \(v\) 连通的所有点中第 \(k(1 \le k \le 10)\) 大的点编号。如果和点 \(v\) 连通的点的数量小于 \(k\) 个,输出-1
。注:节点自身与自身连通。
解题思路
连通分量可以用并查集维护,注意到 \(k \le 10\),每个连通分量再附加维护一个有序的 vector
,至多存储 \(10\) 个节点的编号。合并的时候执行归并排序的流程,因此合并的时间复杂度 \(O(k)\),查询复杂度 \(O(1)\)。
如果 \(k\) 比较大的话,可以参照 官方题解 优化。
参考代码
#include <iostream>
#include <vector>
using namespace std;
const int MAXK = 10;
struct DSU
{
vector<int> parent_or_size;
vector<vector<int>> top_k;
DSU(int n) : parent_or_size(n, -1), top_k(n)
{
top_k.reserve(n);
for (int i = 0; i < n; i++)
top_k[i].push_back(i);
}
int leader(int u) { return parent_or_size[u] < 0 ? u : parent_or_size[u] = leader(parent_or_size[u]); }
void merge(int u, int v)
{
u = leader(u);
v = leader(v);
if (u == v)
return;
if (-parent_or_size[u] > -parent_or_size[v])
swap(u, v);
const vector<int> &tu = top_k[u];
const vector<int> &tv = top_k[v];
int us = tu.size(), i = 0;
int vs = tv.size(), j = 0;
int len = min(MAXK, us + vs), k = 0;
vector<int> t(len);
while (i < us && j < vs && k < len)
t[k++] = tu[i] > tv[j] ? tu[i++] : tv[j++];
while (i < us && k < len)
t[k++] = tu[i++];
while (j < vs && k < len)
t[k++] = tv[j++];
parent_or_size[v] += parent_or_size[u];
parent_or_size[u] = v;
top_k[v] = t;
}
int kth(int u, int k)
{
u = leader(u);
if (-parent_or_size[u] < k)
return -1;
return top_k[u][k - 1];
}
};
int main()
{
int n, q;
cin >> n >> q;
DSU dsu(n);
while (q--)
{
int op;
cin >> op;
if (op == 1)
{
int u, v;
cin >> u >> v;
u--;
v--;
dsu.merge(u, v);
}
else
{
int u, k;
cin >> u >> k;
u--;
cout << dsu.kth(u, k) + 1 << endl;
}
}
return 0;
}
F - Teleporting Takahashi 2¶
题目大意
给你一个 \(N(2 \le N \le 2 \times 10 ^ 5)\) 个点,\(N+M\) 条边的有向图,图中有两类边:
- 第一类边:\(1 \rightarrow 2, 2 \rightarrow 3, \cdots, N-1 \rightarrow N, N \rightarrow 1\),这种边一共有 \(N\) 条。
- 第二类边:输入 \(M\) 对整数 \((X_i, Y_i)\),表示存在一条 \(X_i \rightarrow Y_i\) 的边。这种边一共有 \(M(1 \le M \le 50)\) 条。
问:从点 \(1\) 出发,有多少条长度为 \(K\) 的路径?答案对 \(998244353\) 取模。
解题思路
考虑到 \(M \le 50\),所以第二种类型的边是非常少的,图中大多数的点 \(i\) 只有 \(i \rightarrow i+1\) 一条边,一旦访问到对于这样类型的点,只能一路 \(i \rightarrow i+1 \rightarrow i+2 \rightarrow \cdots\),所以考虑对图中的点进行缩点处理,把相邻的出度和入度为 \(1\) 的点缩成一个,缩点的同时记录下每个点的集合大小。显然锁完点之后图中最多有 \(4 \times M\) 个点,然后考虑 \(dp\),定义 \(dp[i][j]\) 表示走到点 \(i\) 的长度为 \(j\) 的路径数量,按照定义后向的传播即可。
实现的时候要注意点 \(1\) 不能被缩,其次就是特殊处理最终结果停留在某条链的中间的情况。
参考代码
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
int main()
{
int n, m, k;
cin >> n >> m >> k;
vector<int> x(m), y(m);
vector<bool> deg(n + 1);
for (int i = 0; i < m; i++)
{
cin >> x[i] >> y[i];
deg[x[i]] = deg[y[i]] = true;
}
int idx = 0;
vector<int> p(n + 1), cnt(n + 1, 1);
p[1] = ++idx;
for (int u = 2; u <= n;)
{
p[u] = ++idx;
if (!deg[u])
{
int v = u + 1;
while (v <= n && !deg[v])
{
p[v++] = p[u];
cnt[p[u]]++;
}
u = v;
}
else
u++;
}
vector<vector<int>> edge(idx + 1);
for (int i = 0; i < m; i++)
{
int u = p[x[i]], v = p[y[i]];
edge[u].push_back(v);
}
for (int u = 1; u < idx; u++)
edge[u].push_back(u + 1);
edge[idx].push_back(1);
vector<vector<int>> dp(k + 1, vector<int>(idx + 1));
dp[0][1] = 1;
for (int i = 0; i < k; i++)
for (int u = 1; u <= idx; u++)
{
if (dp[i][u] == 0)
continue;
for (auto v : edge[u])
{
int j = min(k, i + cnt[v]);
dp[j][v] = (dp[j][v] + dp[i][u]) % MOD;
}
}
int ans = 0;
for (int u = 1; u <= idx; u++)
ans = (ans + dp[k][u]) % MOD;
cout << ans << endl;
return 0;
}
创建日期: 2024-12-08