题目:Kick Start 2021 Round-B .

Increasing Substring

输出字符串中每个字符的最长 Increasing Substring 的长度,非常简单的动态规划问题。

定义 dp[i] 是以 str[i] 结尾的最长 Increasing Substring 的长度。

转移方程

dp[i] = dp[i-1] + 1, if str[i-1] < str[i]
dp[i] = 1, otherwise

显然是可以进行空间优化的,然而「可以但没必要」。

代码实现

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int cnt = 1;
void solve(string &str, int n)
{
vector<int> dp(n, 1);
for (int i = 1; i < n; i++)
{
if (str[i - 1] < str[i])
dp[i] = dp[i - 1] + 1;
}
printf("Case #%d:", cnt++);
for (int x : dp) printf(" %d", x);
printf("\n");
}
int main()
{
int t, n;
cin >> t;
cin.ignore();
while (t--)
{
string str;
cin >> n; cin.ignore();
cin >> str; cin.ignore();
solve(str, n);
}
}

Longest Progression

给定一个数组 \(A[n]\) ,在允许改动一个元素的条件下,找到最长的等差数列的长度(这个数列在数组中必须是连续的)。

令:

  • left[i] 表示从位置 i 向左延伸,能够得到的最长等差数列的长度(包含 a[i] );
  • right[i] 表示从位置 i 向右延伸,能够得到的最长等差数列的长度(包含 a[i] )。

显然,如果我们允许改动一个位置,那么扫描数组中的任意一个数 \(a_i\) ,判断改动 \(a_i\) 是否能组合得到一个更长的等差数列:

  • 组合 left[i-1]right[i+1]

    • 条件为:\(a_{i+2} - a_{i+1} = a_{i-1} - a_{i-2} \text{ and } a_{i+1} - a_{i-1} = 2(a_{i-1} - a_{i-2})\)
  • 组合 left[i-1]a[i], a[i+1]
    • 条件为:\(a_{i+1} - a_{i-1} = 2(a_{i-1} - a_{i-2})\)
  • 组合 right[i+1]a[i], a[i-1]
    • 条件为:\(a_{i+1} - a_{i-1} = 2(a_{i+2} - a_{i+1})\)
  • 组合 left[i-1]a[i] ,或者组合 right[i+1]a[i] ,二者是必然能实现的,无需任何条件。

代码实现

#include <iostream>
#include <vector>
using namespace std;
int cnt = 1;
int solve(int n, vector<int> &a)
{
if (n <= 3) return n;
vector<int> left(n, 2), right(n, 2);
left[0] = right[n - 1] = 1;
for (int i = 2; i < n; i++)
if ((a[i] - a[i - 1]) == (a[i - 1] - a[i - 2]))
left[i] = left[i - 1] + 1;
for (int i = n - 3; i >= 0; i--)
if ((a[i + 2] - a[i + 1]) == (a[i + 1] - a[i]))
right[i] = right[i + 1] + 1;
int ans = max(left[n - 2] + 1, right[1] + 1);
for (int i = 1; i < n - 1; i++)
{
// left[i-1] + 1 其实就是组合 left[i-1] 和 a[i], 因为允许改动 a[i], 所以这是必然能实现的
// right[i+1] 与之同理
ans = max(ans, max(left[i - 1] + 1, right[i + 1] + 1));
if (i >= 2 && a[i + 1] - a[i - 1] == 2 * (a[i - 1] - a[i - 2]))
ans = max(ans, left[i - 1] + 2);
if (i + 2 < n && a[i + 1] - a[i - 1] == 2 * (a[i + 2] - a[i + 1]))
ans = max(ans, right[i + 1] + 2);
if (i >= 2 && i + 2 < n &&
a[i + 1] - a[i - 1] == 2 * (a[i - 1] - a[i - 2]) &&
a[i - 1] - a[i - 2] == a[i + 2] - a[i + 1])
ans = max(ans, left[i - 1] + right[i + 1] + 1);
}
return ans;
}
int main()
{
ios::sync_with_stdio(0);
int t, n;
cin >> t;
while (t--)
{
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
printf("Case #%d: %d\n", cnt++, solve(n, nums));
}
}

Consecutive Primes

给定一个整数 \(n\) ,求两个相邻的素数 \(l, r\) ( \(l \cdot r \le n\) ) ,且使得 \(l \cdot r\) 的乘积最大,输出这个最大乘积。

思路

  • 令 \(k = \sqrt{n}\) , 求出 \(k\) 左侧的最大素数为 \(l\) ,\(k\) 右侧的最小素数为 \(r\) 。
  • 如果 \(l \cdot r \le n\) ,那么返回 \(l \cdot r\) 。
  • 否则,存在 \(l_2 < l\) ,\(l_2\) 是小于 \(l\) 的最大素数,返回 \(l_2 \cdot l\) 。

正确性证明

  • 最理想的情况是 \(k = \sqrt{n}\) 为一个整数,那么 \(l = r = \sqrt{n}\) 可以得到最大乘积 \(n\) 。但题目要求为 2 个相邻的不同素数。
  • 因此,这 2 个素数必然是下面 2 种情况之一(否则不能保证 \(l \cdot r \le n\) ):
    • 一个在 \(k\) 的左侧,一个在 \(k\) 的右侧。
    • 两个都在 \(k\) 的左侧。
  • 显然,如果「一左一右」的情况存在,它的乘积必然大于「均在左侧」这个乘积,因为 \(l_2 < l < r\)。

时间复杂度

  • 素数判定可以在 \(O(\sqrt{k})\) 内完成。
  • 根据 Prime Gap ,两个相邻素数 \(p_{i}, p_{i+1}\) 之差可以记为 \(g_i\) .
  • 最坏情况下,我们需要找到 3 个相邻的素数(需要扫描 2 个 Prime Gap),因此算法复杂度为 \(O((g_l + g_r) \cdot \sqrt{k})\),\(k = \sqrt{n}\) .
  • 题目给定 \(n \le 10^{18}\) ,因此 \(k \le 10^9\) 。查表得 \(g_l, g_r\) 在 282 - 288 之间,因而这一算法复杂度是可以接受的。

代码实现

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int cnt = 1;
bool isprime(uint64_t k)
{
for (uint64_t i = 2; i * i <= k; i++)
if (k % i == 0) return false;
return true;
}
uint64_t solve(uint64_t n)
{
uint64_t k = sqrt(n);
uint64_t l = k, r = k + 1;
while (!isprime(l)) l--;
while (!isprime(r)) r++;
if (l * r <= n) return l * r;
uint64_t l2 = l - 1;
while (!isprime(l2)) l2--;
return l * l2;
}
int main()
{
int t;
cin >> t;
cin.ignore();
while (t--)
{
uint64_t n;
cin >> n;
cin.ignore();
printf("Case #%d: %llu\n", cnt++, solve(n));
}
}

Truck Delivery

给定一个树 \(G\) ,每个顶点 \(1-n\) 代表一个城市,每个边代表一个公路,公路有 2 个参数 (limit, amount) 。如果经过这一公路的卡车,它的 weight 大于等于 limit ,那么需要收费 amount ,否则不收费。

问:给定一个 \(Q\) ,表示工作的天数,每一天有 2 个参数 \(Q_i = (C, W)\) ,表示从城市 \(C\) 出发,目的地是城市 \(1\) ,显然这样的路径是唯一的。卡车的 weight 为 \(W\) ,那么从 \(C \rightarrow 1\) 的这一路径上,每个公路都对应一个收费。对于每个 \(Q_i\),求这一天中,所有收费的最大公因子。

BFS/DFS

最简单,也是最暴力的解法。

思路

  • 建图完成后,执行 bfs(1) ,从城市 \(1\) 开始 BFS,找到 \(1\) 到其他城市 \(2-n\) 的所有路径。路径通过一个数组 pre 记录,pre[x] 表示 x 的前驱城市。

  • 对于每一个 \(Q_i = (C,W)\) ,找到从 \(C \rightarrow 1\) 的路径,并找到所有 amount 的最大公因子。

  • 时间复杂度为 \(O(N + Q(N + \log{A}))\) , \(A\) 是 amount 的最大值。

  • 显然,这个复杂度对于 Test Case 2 来说是不可接受的。

  • 第一步的 BFS 也可以换成 DFS ,因为在树中,只要遍历一次,即可找到 \(1\) 到 \(2-n\) 的路径。

代码实现

被数据范围搞死了,amount 的范围是 \(10^{18}\) ,因此必须用 uint64_t ,我改了数据范围,但忘了改 ans 的类型;改了 ans 的类型,但忘了改 gcd 的参数;改了 gcd 的参数,但忘了改 gcd 的返回值。最后逼得我 Ctrl+F 把 int 全部替换为 uint64_t

#include <iostream>
#include <vector>
#include <unordered_map>
#include <queue>
using namespace std;
// 'first' is load-limit, 'second' is amount
typedef pair<uint64_t, uint64_t> node_t;
uint64_t cnt = 1;
unordered_map<uint64_t, unordered_map<uint64_t, node_t>> graph;
uint64_t gcd(uint64_t a, uint64_t b) { return b == 0 ? a : gcd(b, a % b); }
vector<uint64_t> bfs(uint64_t n, uint64_t start)
{
vector<uint64_t> vis(n + 1, false), pre(n + 1, 0);
queue<uint64_t> q;
vis[start] = true, q.push(start);
while (!q.empty())
{
uint64_t vex = q.front();
q.pop();
for (auto &p : graph[vex])
{
uint64_t adjacent = p.first;
if (!vis[adjacent])
vis[adjacent] = true, q.push(adjacent), pre[adjacent] = vex;
}
}
return move(pre);
} int main()
{
ios::sync_with_stdio(0);
uint64_t t, n, q;
uint64_t x, y, limit, amount, city, weight;
cin >> t;
while (t--)
{
cin >> n >> q;
graph.clear();
for (uint64_t i = 0; i < n - 1; i++)
{
cin >> x >> y >> limit >> amount;
graph[x][y] = graph[y][x] = {limit, amount};
}
auto pre = bfs(n, 1);
cout << "Case #" << cnt++ << ": ";
while (q--)
{
cin >> city >> weight;
uint64_t cur = city, ans = 0;
while (cur != 1)
{
uint64_t prev = pre[cur];
auto [limit, amount] = graph[prev][cur];
if (weight >= limit) ans = gcd(amount, ans);
cur = prev;
}
cout << ans << " ";
}
cout << '\n';
}
}

Segement Tree

官方题解使用了线段树 (Segement Tree) ,但我不会这个数据结构 , TO BE DONE.

[Kick Start] 2021 Round B的更多相关文章

  1. [Kick Start] 2021 Round A

    题目:2021 Round-A . K-Goodness String 签到题,计算当前字符串的 K-Goodness Score ,然后与给出的 K 做差即可. #include <iostr ...

  2. kick start 2019 round D T3题解

    ---恢复内容开始--- 题目大意:共有N个房子,每个房子都有各自的坐标X[i],占据每个房子需要一定花费C[i].现在需要选择K个房子作为仓库,1个房子作为商店(与题目不同,概念一样),由于仓库到房 ...

  3. kick start 2019 round D T2题解

    题目大意:由N个房子围成一个环,G个人分别顺时针/逆时针在房子上走,一共走M分钟,每分钟结束,每个人顺/逆时针走到相邻的房子.对于每个房子都会记录最后时刻到达的人(可能是一群人).最终输出每个人会被几 ...

  4. Kick Start 2019 Round A Contention

    $\DeclareMathOperator*{\argmax}{arg\,max}$ 题目链接 题目大意 一排 $N$ 个座位,从左到右编号 $1$ 到 $N$ . 有 $Q$ 个预定座位的请求,第 ...

  5. Kick Start 2019 Round H. Elevanagram

    设共有 $N = \sum_{i=1}^{9} A_i$ 个数字.先把 $N$ 个数字任意分成两组 $A$ 和 $B$,$A$ 中有 $N_A = \floor{N/2}$ 个数字,$B$ 中有 $N ...

  6. Kick Start 2019 Round A Parcels

    题目大意 $R \times C$ 的网格,格子间的距离取曼哈顿距离.有些格子是邮局.现在可以把至多一个不是邮局的格子变成邮局,问每个格子到最近的邮局的曼哈顿距离的最大值最小是多少. 数据范围 $ 1 ...

  7. Kick Start 2019 Round B Energy Stones

    对我很有启发的一道题. 这道题的解法中最有思维难度的 observation 是 For simplicity, we will assume that we never eat a stone wi ...

  8. 【DP 好题】Kick Start 2019 Round C Catch Some

    题目链接 题目大意 在一条数轴上住着 $N$ 条狗和一个动物研究者 Bundle.Bundle 的坐标是 0,狗的坐标都是正整数,可能有多条狗住在同一个位置.每条狗都有一个颜色.Bundle 需要观测 ...

  9. Kick Start 2019 Round F Teach Me

    题目链接 题目大意 有 $N$ 个人,$S$ 项技能,这些技能用 $1, 2, 3, \dots, S$ 表示 .第 $i$ 个人会 $c_i$ 项技能($ 1 \le c_i \le 5 $).对于 ...

随机推荐

  1. IT菜鸟之虚拟机VMware的使用

    虚拟机安装完成了,以下是虚拟机的使用. 双击快捷方式,打开vmware虚拟机. 点击创建新虚拟机,这里可以选择创建方式,可以点击典型并一路下一步创建,我们这里讲自定义创建. 这里选择兼容版本,大家可以 ...

  2. 最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示。 HardInfo 显示硬件具体信息,甚至包括一组八个的流行的性能基准程序,你可以用它们评估你的系统性能。 KInfoCenter 和 Lshw 也能够显示硬件的详细信息,并且可以从许多软件仓库中获取。

    最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示. HardInfo 显示硬件具体信息,甚至包括一组八个的流 ...

  3. 云计算OpenStack共享组件---信息队列rabbitmq(2)

    一.MQ 全称为 Message Queue, 消息队列( MQ ) 是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们. 消息传 ...

  4. numpy tile()函数

    tile(A,B)即在B的方向上,重复A 直接举栗子: A=[1,2] tile(A,2) 此时B=(2) ,B的方向仅包含列方向,将A在列方向上重复一次,得出结果如图1所示   图1-将A在列方向重 ...

  5. Swift系列十 - inout的本质

    inout是可以用来在函数内部修改外部属性内存的. 一.inout回顾 示例代码: func test(_ num: inout Int) { num = 20 } var a = 10 test(& ...

  6. Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[]] 错误(Day_25)

    错误:    在maven项目,web启动的时候报这个错误 Failed to start component [StandardEngine[Tomcat].StandardHost[localho ...

  7. 解决Maven资源过滤

    <build> <resources> <resource> <directory>src/main/java</directory> &l ...

  8. 西门子S7系列以太网通讯处理器安装调式操作

    北京华科远创科技有限研发的远创智控ETH-YC模块,PLC转以太网型号有MPI-ETH-YC01和MPI-ETH-YC01,适用于西门子S7-200/S7-300/S7-400.SMART S7-20 ...

  9. Web应用漏洞-NGINX各类请求头缺失对应配置

    前言 随着越来越多的网络访问通过WEB界面进行操作,WEB安全已经成为互联网安全的一个热点,基于WEB的攻击广为流行,SQL注入.跨站脚本等WEB应用层漏洞的存在使得网站沦陷.页面篡改.网页挂马等攻击 ...

  10. AIoT开放平台及应用

    AIoT开放平台及应用 阿里AIoT开放平台,是阿里云IoT面向开发者的能力接入渠道,开发者可以在这里完成能力的申请.开通.部署.配置和集成开发等一些列工作.这些能力并不的独立交付,而是通过关联到行业 ...