感觉难度不是很难的一场。
A. 排序危机(思维,模拟)
思路
问题本质就是从给出的字符串中挑出三种字符的子序列,然后按小写,数字,大写的顺序输出子序列,因此,可以从前往后遍历字符串,每遍历到一个字符就把它加入它所属的子序列的末尾,这样就保证了题目要求的“交换后同类字符的相对位置也是不变的”。
复杂度
时间复杂度 ,空间复杂度
代码实现
// Problem: 排序危机
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/95016/A
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
void solve()
{
int n;
string s1, s2, s3, s;
cin >> n >> s;
for (int i = 0; i < n; i++) {
if (islower(s[i]))
s1 += s[i];
else if (isupper(s[i]))
s3 += s[i];
else
s2 += s[i];
}
cout << s1 << s2 << s3;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
B. 小歪商店故事:卷(简单数学)
思路
因为 ,所以
。
因为 是整数又满足上述性质,所以
。
复杂度
时间复杂度 ,空间复杂度
代码实现
// Problem: 小歪商店故事:卷
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/95016/B
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
void solve()
{
int a, b, c, d;
cin >> a >> b >> c >> d;
int am = (b * c + d - 1) / d - 1;
cout << a - am << ' ';
}
// a/b<c/d a<bc/d
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
C. 小苯的计算式(枚举)
思路
因为 最大为
,所以可以从
到
枚举
,然后
,再看对应的式子长度是否为
,如果是的话那么当前符号条件的式子数量
就加一,最后
就是所求答案。
复杂度
时间复杂度 ,空间复杂度
代码实现
// Problem: 小苯的计算式
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/95016/C
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
void solve()
{
int n, c;
cin >> n >> c;
int ans = 0;
int m = to_string(c).size();
for (int a = 0; a <= c; a++) {
string sa = to_string(a);
string sb = to_string(c - a);
if (sa.size() + sb.size() + m + 2 == n)
ans++;
}
cout << ans;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
D. K(构造,思维)
思路
如果有 个极大不同区间,那么极大不同区间的最大长度为
。
当 时,极大不同区间长度为负数,这是不可能的,因此当
时无解。
对于有解的情况,不妨让极大不同区间长度为 ,这样的话,对于一个极大不同区间
,它的下一个极大不同区间就是
,区间中元素的变化就是丢弃了
,增加了
。
如果 ,那么区间
将会可能是更大的极大不同区间,这样就发生冲突了,所以要令
。
因此,令 ,如果
,
,(第一个极大不同区间设置为
),否则
。
复杂度
时间复杂度 ,空间复杂度
代码实现
// Problem: K
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/95016/D
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
int n, k, a[N];
void solve()
{
cin >> n >> k;
if (k > n) {
cout << "NO";
} else {
cout << "YES\n";
int m = n - k + 1;
for (int i = 1; i <= n; i++) {
if (i <= m)
a[i] = i;
else
a[i] = a[i - m];
cout << a[i] << ' ';
}
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}
// k > n 无解
// 区间数量为k,区间长度最大为 n-k+1
E. 小苯的区间选数(枚举,贪心)
思路
因为 是在
和
中选一个数相加得到的,所以
。
令 。
如果
,那么
从
的最高位向下若干位会与
相同,然后在第一个不同的位置上,
对应位置上的数字小于
对应位置上的数字。
如果 与
第一个不同的位置为
,那么
之后的位置可以都放
,使得数位和尽可能大。
因为数位不会很多,所以可以从高到低枚举第一个不同的位置,若当前枚举到位置 ,
从最高位到第
位构成的数字位
,
的数位和为
,如果
,那么此时数位和最大的
,对应的数位和为
。(这里数位
是从
开始计算的,数位
为从低到高第
个位置)。
如果 ,那么就将
与当前记录的最大数位和
比较保留最大值。
还有一种 的情况,可以初始化的时候把
置为
的数位和,这样
的数位和也参与进了比较中。
复杂度
时间复杂度 ,空间复杂度
代码实现
// Problem: 小苯的区间选数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/95016/E
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
int pre[N];
// pre[i] = 10^i
void init()
{
pre[0] = 1;
for (int i = 1; i <= 18; i++) {
pre[i] = pre[i - 1] * 10;
}
}
// 分解出 x 每个数位上的数字
vector<int> div(int x)
{
vector<int> res;
while (x) {
res.push_back(x % 10);
x /= 10;
}
return res;
}
void solve()
{
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
int l = l1 + l2, r = r1 + r2;
vector<int> nums = div(r);
int ans = 0, now = 0, sum = 0;
int n = nums.size();
for (int x : nums)
ans += x;
for (int i = n - 1; i >= 0; i--) {
if (nums[i]) {
int val = (now * 10 + nums[i] - 1) * pre[i] + pre[i] - 1;
if (val >= l)
ans = max(sum + nums[i] - 1 + i * 9, ans);
}
now = now * 10 + nums[i];
sum += nums[i];
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
init();
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
F. 小Z的树迁移(预处理,离线,树上启发式合并)
思路
令 为从根节点
到节点
路径上边的权值和,
为节点
的深度。
如果 为
的父节点,
为边
的权值,则
。
其实
和
本质上是一样的,
相当与
的情况,
。
如果节点 向下移动
次移动到
,
的深度
显然为
,从
到
路径上的权值和为
。(类似前缀和)。
对于每个查询,其实就是在以 为根的子树中,找到满足深度为
的节点
,对应的
的最大值,因为
是确定的,所以是要找
的最大值。
当搜索完以 为根的子树后,可以得到该子树内所有不同深度对应的最大的
。举个例子,子树中深度为
的所有节点为
,可以得到子树中深度为
的
的最大值为
。
在回溯到 的父节点
时,可以把在搜索完以
子树后,得到该子树内所有不同深度对应的最大的
传递上去。
如果 恰好要进行深度为
的查询,就可以从
传递上来的信息中看有没有深度为
对应的
最大值。
如果因为 会有多个子节点
,所以可能存在有多个子树中深度为
对应的
最大值,应该取其中的最大值。
换句话说,对于每个节点 ,要维护一个二元组集合
,其中的元素为
。
当搜索完 的子节点
,就将
和
的集合进行合并,合并时,若两个元素的深度相同,保留对应
最大值的最大的元素,最后
的集合中元素就代表着以
为根的子树内某种深度对应的
最大值,就可以进行关于
的查询。
为确保合并时的复杂度不会过高,采用启发式合并,就是每次都是将小集合合并入大集合,换句话说,就是每次遍历小集合,将遍历到的元素加入大集合。
因为是将小集合合并入大集合,相当与小集合的大小翻倍了,因为集合最后的大小为 ,所以原先小集合的部分被遍历次数最多就是
,共有
个元素,所以这样合并的最坏复杂度为
。
复杂度
时间复杂度 ,空间复杂度
代码实现
// Problem: 小Z的树迁移
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/95016/F
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n, m, idx;
int ans[N], dep[N], pre[N];
vector<array<int, 2>> h[N], qry[N];
int p[N];
unordered_map<int, int> val[N];
int find(int x)
{
return p[x] != x ? p[x] = find(p[x]) : x;
}
void merge(int u, int v)
{
u = find(u), v = find(v);
if (u != v) {
if (val[u].size() > val[v].size())
swap(u, v);
// 小集合val[u] 合并入大集合val[v]
for (auto it : val[u]) {
int x = it.first, y = it.second;
val[v][x] = max(val[v][x], y);
}
p[u] = v;
}
}
void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
for (auto it : h[u]) {
int v = it[0], w = it[1];
if (v == fa)
continue;
pre[v] = pre[u] + w;
dfs(v, u);
merge(u, v);
}
int pu = find(u);
val[pu][dep[u]] = pre[u];
// 以u为根的子树只有u深度为dep[u],直接赋值即可
for (auto it : qry[u]) {
int ds = dep[u] + it[0], id = it[1];
if (!val[pu].count(ds)) {
ans[id] = -1;
// 若没有对应深度则无解
} else {
ans[id] = val[pu][ds] - pre[u];
}
}
}
void solve()
{
cin >> n;
for (int i = 1; i < n; i++) {
int u, v, w;
cin >> u >> v >> w;
h[u].push_back({ v, w });
h[v].push_back({ u, w });
}
for (int i = 1; i <= n; i++) {
p[i] = i;
}
cin >> m;
for (int i = 1; i <= m; i++) {
int u, d;
cin >> u >> d;
qry[u].push_back({ d, i });
}
dfs(1, 0);
for (int i = 1; i <= m; i++)
cout << ans[i] << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
}