AtCoder Beginner Contest 338D - Island Tour【枚举】

2024-01-28 23:20

本文主要是介绍AtCoder Beginner Contest 338D - Island Tour【枚举】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原题链接:https://atcoder.jp/contests/abc338/tasks/abc338_d

Time Limit: 2 sec / Memory Limit: 1024 MB

Score: 425 points

问题陈述

AtCoder 群岛由 N 座岛屿组成,这些岛屿由 N 座桥梁连接。这些岛屿的编号从1到N,i(1≤i≤N−1)桥双向连接i和i+1岛,而N桥双向连接N和1岛。除了过桥之外,没有其他方式可以在岛屿之间旅行。

在这些岛屿上,经常会有从X1​岛出发,依次游览X2​,X3​,…,XM​岛的旅行团。游览过程中可能会经过正在游览的岛屿以外的其他岛屿,游览过程中经过桥梁的总次数定义为游览的长度*。

更确切地说,是满足以下所有条件的l+1岛屿a0​,a1​,…,al​序列,其长度定义为l:

  • 对于所有j (0≤j≤l−1),岛屿aj​和aj+1​都由一座桥直接连接。
  • 有一些 0=y1​<y2​<⋯<yM​=l,对于所有 k (1≤k≤M),ayk​​=Xk​。

由于财政困难,这些岛屿将关闭一座桥,以减少维护费用。求在最佳情况下选择关闭的桥时,可能的最小游程长度。

限制因素

  • 3≤N≤2×1e5
  • 2≤M≤2×1e5
  • 1≤Xk​≤N
  • Xk​\neqXk+1​ (1≤k≤M−1)
  • 所有输入值均为整数。

输入输出描述

Sample Input 1Copy

3 3
1 3 2

Sample Output 1Copy

2
  • 如果第一座桥关闭:以岛屿 (a0​,a1​,a2​)=(1,3,2) 为序列,可以依次游览岛屿 1,3,2,可以进行长度为 2 的游览。没有更短的游程。
  • 如果第二座桥关闭:根据岛屿(a0​,a1​,a2​,a3​)=(1,3,1,2)的顺序,可以依次游览岛屿1,3,2,可以进行长度为3的游览。没有更短的游程。
  • 如果第三座桥关闭:按照岛屿(a0​,a1​,a2​,a3​)=(1,2,3,2)的顺序,可以依次游览岛屿1,3,2,可以进行长度为3的游览。没有更短的游程。

因此,在最佳选择关闭桥梁的情况下,可能的最小游程长度为 2。

下图从左到右分别显示了关闭桥梁 1,2,3 时的情况。带数字的圆圈代表岛屿,连接圆圈的线代表桥梁,蓝色箭头代表最短旅游路线。

Sample Input 2Copy

4 5
2 4 2 4 2

Sample Output 2Copy

8

同一岛屿可能在 X1​,X2​,…,XM​ 中出现多次。

Sample Input 3Copy

163054 10
62874 19143 77750 111403 29327 56303 6659 18896 64175 26369

Sample Output 3Copy

390009

解题思路:

先画个图来描述一下:

如上图所示例子,对于任意俩个a-b,从a到b和从b到a走过的路径一定是一样的,例如[2,4],从2走到4和从4走到2走的路径的长度一定是一样的,我们不妨让x=min(a,b],y=max(a,b),对于任意一条路径a<->b<->c<->d,任意俩个点可以看作一个区间,较小的那个数为左端点,较大的那个数为右端点,我们定义俩个vector数组l,r,r[i]存储右端点为i的所有区间的左端点,l[i]存储左端点为i的所有区间的右端点,然后枚举删每一条边, 然后看当前删除的边会对哪些区间的计算造成影响,首先考虑删除n号结点到1号结点之间的边,如上图所示就是删除6号边,那么此时任意区间的计算方式都是右端点减去左端点,此时计算这种删边方式走过的路径的长度,然后考虑删除i号点和i+1号点之间的i号边,然后考虑此时会对原来的区间造成哪些影响。

  • 首先对于左端点位于i号结点的区间,那么这个区间的右端点肯定位于i号点右边,由于i号边被删除了,那么这个区间的贡献计算方式就不是右端点-左端点了,应该是先右端点走到n号点,n号点再走到1号点,然后1号点再走到左端点,所以把原来的右端点减去左端点的贡献减去,把新的贡献加上。
  • 然后对于右端点位于i号结点的区间,那么这个区间的左端点肯定位于i号点左边,由于i号边被删除了,那么这个区间的计算方式就不是先右端点走到n号点,n号点在走到1号点,然后1号点再走到左端点了,而是直接从左端点走到右端点,所以把原来的右端点->n->1->左端点的贡献减去,加上新的贡献右端点-左端点。

这样枚举删每一条边了,根据造成的影响修改贡献,然后对于所有删边情况的总贡献求一个最小值即可。

时间复杂度:枚举删除每条边时间复杂度为O(n),然后每个区间只会被使用俩次,当遇到左端点的时候使用一次,遇到右端点的时候使用一次,时间复杂度为O(m),最终时间复杂度为O(n+m)。

空间复杂度:定义了俩个vector数组l,r,l[i]表示左端点为i的所有区间的右端点,r[i]表示右端点为i的所有区间的左端点,每个区间左端点右端点各存储一次,所以空间复杂度为O(n+m)。

cpp代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>using namespace std;
typedef long long LL;
typedef pair<int, int> PII;const int N = 2e5 + 10;
int n, m;
int a[N];
vector<int> r[N], l[N]; // l[i]表示左端点为i的所有区间的右端点,r[i]表示右端点为i的所有区间的左端点
int main()
{cin >> n >> m;for (int i = 1; i <= m; i++)scanf("%d", &a[i]);LL ans = 0;for (int i = 2; i <= m; i++){int x = a[i - 1], y = a[i];if (x > y)swap(x, y);    // 让x表示左端点,y表示右端点ans += y - x;      // 开始删n号边,所有区间贡献的计算方式都是右端点-左端点r[y].push_back(x); // 存储右端点是y的所有区间l[x].push_back(y); // 存储左端点是x的所有区间}LL sum = ans;for (int i = 1; i <= n; i++) // 枚举删每一条边{for (auto t : r[i]){ // 对于右端点为i的所有区间根据删除的边修改贡献sum += i - t;sum -= (n - i + t);}for (auto t : l[i]){ // 对于左端点为i的所有区间根据删除的边修改贡献sum -= t - i;sum += (n - t + i);}ans = min(ans, sum); // 更新答案}cout << ans << endl;return 0;
}

这篇关于AtCoder Beginner Contest 338D - Island Tour【枚举】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/655117

相关文章

hdu 2489 (dfs枚举 + prim)

题意: 对于一棵顶点和边都有权值的树,使用下面的等式来计算Ratio 给定一个n 个顶点的完全图及它所有顶点和边的权值,找到一个该图含有m 个顶点的子图,并且让这个子图的Ratio 值在所有m 个顶点的树中最小。 解析: 因为数据量不大,先用dfs枚举搭配出m个子节点,算出点和,然后套个prim算出边和,每次比较大小即可。 dfs没有写好,A的老泪纵横。 错在把index在d

2014 Multi-University Training Contest 8小记

1002 计算几何 最大的速度才可能拥有无限的面积。 最大的速度的点 求凸包, 凸包上的点( 注意不是端点 ) 才拥有无限的面积 注意 :  凸包上如果有重点则不满足。 另外最大的速度为0也不行的。 int cmp(double x){if(fabs(x) < 1e-8) return 0 ;if(x > 0) return 1 ;return -1 ;}struct poin

2014 Multi-University Training Contest 7小记

1003   数学 , 先暴力再解方程。 在b进制下是个2 , 3 位数的 大概是10000进制以上 。这部分解方程 2-10000 直接暴力 typedef long long LL ;LL n ;int ok(int b){LL m = n ;int c ;while(m){c = m % b ;if(c == 3 || c == 4 || c == 5 ||

2014 Multi-University Training Contest 6小记

1003  贪心 对于111...10....000 这样的序列,  a 为1的个数,b为0的个数,易得当 x= a / (a + b) 时 f最小。 讲串分成若干段  1..10..0   ,  1..10..0 ,  要满足x非递减 。  对于 xi > xi+1  这样的合并 即可。 const int maxn = 100008 ;struct Node{int

hdu 6198 dfs枚举找规律+矩阵乘法

number number number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description We define a sequence  F : ⋅   F0=0,F1=1 ; ⋅   Fn=Fn

【Rust练习】12.枚举

练习题来自:https://practice-zh.course.rs/compound-types/enum.html 1 // 修复错误enum Number {Zero,One,Two,}enum Number1 {Zero = 0,One,Two,}// C语言风格的枚举定义enum Number2 {Zero = 0.0,One = 1.0,Two = 2.0,}fn m

枚举相关知识点

1.是用户定义的数据类型,为一组相关的常量赋予有意义的名字。 2.enum常量本身带有类型信息,即Weekday.SUN类型是Weekday,编译器会自动检查出类型错误,在编译期间可检查错误。 3.enum定义的枚举类有什么特点。         a.定义的enum类型总是继承自java.lang.Enum,且不能被继承,因为enum被编译器编译为final修饰的类。         b.只能定义

AtCoder Beginner Contest 370 Solution

A void solve() {int a, b;qr(a, b);if(a + b != 1) cout << "Invalid\n";else Yes(a);} B 模拟 void solve() {qr(n);int x = 1;FOR(i, n) FOR(j, i) qr(a[i][j]);FOR(i, n) x = x >= i ? a[x][i]: a[i][x];pr2(

【C语言】结构体、枚举、联合体

【C语言】结构体、枚举、联合体 文章目录 @[TOC](文章目录) 前言一、结构体声明1.一般格式2.typedef 重命名结构体类型定义变量 二、结构体数组三、结构体与指针及函数传参四、结构体传参五.结构体在内存的存储六、参考文献总结 前言 使用工具: 1.编译器:VScode 2.C Primer Plus 第六版-1 提示:以下是本篇文章正文内容,下面案例可供参考

10730-Antiarithmetic?【暴力枚举】

水题 求一个序列是否存在3个数按顺序构成等差数列 直接枚举等差数列的差值 时间复杂度降到 n * n / 3 开pos数组记录每个值得为之 楷vis数组记录目前i是否出现过 强行AC 15221397 10730 Antiarithmetic? Accepted C++ 0.035 2015-03-26 12:09:56 #include<cstdio>#include