2021-2022-1 ACM集训队每周程序设计竞赛(3)题解
A - 苹果派
题意:
你有 a 个苹果和 p 个苹果片 你有a个苹果和p个苹果片 你有a个苹果和p个苹果片
1 个苹果可以制作 3 个苹果片 1个苹果可以制作3个苹果片 1个苹果可以制作3个苹果片
2 个苹果片可以制作 1 个苹果派 2个苹果片可以制作1个苹果派 2个苹果片可以制作1个苹果派
问一共可以制作多少个苹果派 问一共可以制作多少个苹果派 问一共可以制作多少个苹果派
0 < = a , p < = 100 0 <= a , p <= 100 0<=a,p<=100
思路:
模拟 模拟 模拟
时间复杂度: O 1 O1 O1
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 2010 , mod = 1e9 + 7 ;signed main()
{int n , p ;cin >> n >> p ;p += 3 * n ;de(p/2) ;return 0;
}
B - 餐馆指南
题意:
你决定写一本介绍好餐馆的书。 你决定写一本介绍好餐馆的书。 你决定写一本介绍好餐馆的书。
你想介绍的餐厅有 N 家 : 餐厅 1 ,餐厅 2 , . . . 餐厅 n 你想介绍的餐厅有N家 : 餐厅1,餐厅2,...餐厅n 你想介绍的餐厅有N家:餐厅1,餐厅2,...餐厅n
1 < = n < = 100. 1 <= n <= 100. 1<=n<=100.
餐厅 i 在 S i 市, 餐厅i在Si市, 餐厅i在Si市,
你对每一家餐厅都有一个得分 p 你对每一家餐厅都有一个得分p 你对每一家餐厅都有一个得分p
1 < = p < = 100 1 <= p <= 100 1<=p<=100
没有两家餐馆得分相同。 没有两家餐馆得分相同。 没有两家餐馆得分相同。
您要按以下顺序介绍餐厅 : 您要按以下顺序介绍餐厅: 您要按以下顺序介绍餐厅:
餐厅按其城市名称的字典顺序排列。 餐厅按其城市名称的字典顺序排列。 餐厅按其城市名称的字典顺序排列。
如果同一个城市有多家餐厅, 如果同一个城市有多家餐厅, 如果同一个城市有多家餐厅,
则按得分降序排列。 则按得分降序排列。 则按得分降序排列。
按照书中介绍的顺序打印餐厅的编号。 按照书中介绍的顺序打印餐厅的编号。 按照书中介绍的顺序打印餐厅的编号。
思路:
结构体自定义排序 结构体自定义排序 结构体自定义排序
时间复杂度: O n l o g n Onlogn Onlogn
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 2010 , mod = 1e9 + 7 ;struct ai{int id , score ; // 编号 , 得分string city ; // 城市名称
}q[M] ;bool cmp(ai a , ai b)
{if(a.city == b.city) return a.score > b.score ; // 城市字典序相等的时候按照得分大于(逆序)排序else return a.city < b.city ; // 否则按照字典序小于(顺序)排序
}signed main()
{int n ;cin >> n ;fer(i,1,n){cin >> q[i].city >> q[i].score ;q[i].id = i ;}sort(q + 1 , q + 1 + n , cmp) ;fer(i,1,n) {cout << q[i].id << "\n" ;}return 0;
}
C - 开关
题意:
我们有“开”和“关”状态的 n 个开关和 m 个灯泡。 我们有“开”和“关”状态的n个开关和m个灯泡。 我们有“开”和“关”状态的n个开关和m个灯泡。
开关编号为 1 到 n , 开关编号为1到n, 开关编号为1到n,
灯泡编号为 1 到 m 。 灯泡编号为1到m。 灯泡编号为1到m。
灯泡 i 连接了 k i 个开关 : 开关 s i 1 , s i 2 , . . . ,和 s i k i 。 灯泡i连接了ki个开关:开关si1,si2,...,和siki。 灯泡i连接了ki个开关:开关si1,si2,...,和siki。
对于灯泡 i 连接的这些开关中如果打开的开关数量 m o d 2 等于 p [ i ] ,灯泡 i 会被点亮。 对于灯泡i连接的这些开关中如果打开的开关数量mod2等于p[i],灯泡i会被点亮。 对于灯泡i连接的这些开关中如果打开的开关数量mod2等于p[i],灯泡i会被点亮。
开关的“开”和“关”状态有多少种组合可以点亮所有灯泡? 开关的“开”和“关”状态有多少种组合可以点亮所有灯泡? 开关的“开”和“关”状态有多少种组合可以点亮所有灯泡?
1 < = n , m < = 10 1 <= n , m <= 10 1<=n,m<=10
1 < = k i < = n 1 <= ki <= n 1<=ki<=n
p [ i ] = 0 / 1 p[i] = 0 / 1 p[i]=0/1
思路:
考虑 n 的范围最大只有 10 考虑n的范围最大只有10 考虑n的范围最大只有10
我们可以暴力枚举所有开关打开或者关闭的状态 我们可以暴力枚举所有开关打开或者关闭的状态 我们可以暴力枚举所有开关打开或者关闭的状态
考虑使用状态压缩来做 考虑使用状态压缩来做 考虑使用状态压缩来做
用一个二进制数来表示当前 n 个开关的打开状态 用一个二进制数来表示当前n个开关的打开状态 用一个二进制数来表示当前n个开关的打开状态
假设现在的二进制数是 010101 假设现在的二进制数是010101 假设现在的二进制数是010101
0 表示没选这个开关, 1 表示选了这个开关 0表示没选这个开关,1表示选了这个开关 0表示没选这个开关,1表示选了这个开关
选了 1 的开关即为第 1 个第 3 个第 5 个 选了1的开关即为第1个第3个第5个 选了1的开关即为第1个第3个第5个
从 0 到 2 n − 1 ( n 个 1 ) 包含了所有的可能性 从0到2^n-1(n个1)包含了所有的可能性 从0到2n−1(n个1)包含了所有的可能性
时间复杂度: O 10 ∗ 10 ∗ 2 10 O10*10*2^{10} O10∗10∗210
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 2010 , mod = 1e9 + 7 ;int n , m ; // n个开关 m个灯泡
vector<int> q[M] ;
int p[M] ;signed main()
{cin >> n >> m ;fer(i,1,m){int k ;sf(k) ;fer(j,1,k){int x ;sf(x) ;q[i].pb(x) ;}}fer(i,1,m) sf(p[i]) ;int res = 0 ; // res 表示最后的答案for(int i = 0 ; i < 1 << n ; i ++) // 枚举所有可能的二进制状态{bool st[11] = {0} ; // 初始化所有开关都关闭for(int j = 0 ; j < n ; j ++) // 判断哪些开关开了{// 如果i这个二进制数的第j位是1的话if(i >> j & 1) st[j+1] = 1 ; // 开关编号从1开始 所以+1}int cnt = 0 ; // cnt表示当前打开的灯泡数量for(int j = 1 ; j <= m ; j ++) // 枚举所有灯泡 判断当前这个灯泡是否打开{int k = 0 ; // k表示当前这个灯泡有几个对应的开关打开for(int z = 0 ; z < q[j].size() ; z ++) // 枚举这个灯泡的所有开关{if(st[q[j][z]]) k ++ ; // 如果这个开关打开了 k ++}if(k % 2 == p[j]) cnt ++ ; // 如果 k % 2 == p[j] 说明这个灯泡被打开了 cnt ++}if(cnt == m) res ++ ; // 如果所有灯泡都打开了 res ++}cout << res << "\n" ;return 0;
}
D - 珠宝
题意:
你的朋友送了你一个序列 D 你的朋友送了你一个序列D 你的朋友送了你一个序列D
这个序列 D 上一共有 n 个珠宝 这个序列D上一共有n个珠宝 这个序列D上一共有n个珠宝
按照从左到右的顺序 按照从左到右的顺序 按照从左到右的顺序
每个珠宝都有它的价值 w [ i ] 每个珠宝都有它的价值w[i] 每个珠宝都有它的价值w[i]
1 < = n < = 50 1 <= n <= 50 1<=n<=50
− 1 e 7 < = w [ i ] < = 1 e 7 -1e7 <= w[i] <= 1e7 −1e7<=w[i]<=1e7
你可以对这些珠宝进行最多 k 次操作 你可以对这些珠宝进行 最多 k次操作 你可以对这些珠宝进行最多k次操作
1 < = k < = 100 1 <= k <= 100 1<=k<=100
每次操作你都可以 每次操作你都可以 每次操作你都可以
操作 A :取出 D 中最左边的宝石,放在手中。当 D 为空时,不能执行此操作。 操作A:取出D中最左边的宝石,放在手中。当D为空时,不能执行此操作。 操作A:取出D中最左边的宝石,放在手中。当D为空时,不能执行此操作。
操作 B :取出 D 中最右边的宝石,放在手中。当 D 为空时,不能执行此操作。 操作B:取出D中最右边的宝石,放在手中。当D为空时,不能执行此操作。 操作B:取出D中最右边的宝石,放在手中。当D为空时,不能执行此操作。
操作 C :选择手中的任意一个宝石并将其插入 D 的左端。如果手中没有宝 操作C:选择手中的任意一个宝石并将其插入D的左端。如果手中没有宝 操作C:选择手中的任意一个宝石并将其插入D的左端。如果手中没有宝 石,则无法执行此操作。 石,则无法执行此操作。 石,则无法执行此操作。
操作 D :选择手中的任意一个宝石并将其插入 D 的右端。如果手中没有宝 操作D:选择手中的任意一个宝石并将其插入D的右端。如果手中没有宝 操作D:选择手中的任意一个宝石并将其插入D的右端。如果手中没有宝 石,则无法执行此操作。 石,则无法执行此操作。 石,则无法执行此操作。
求执行完最多 k 次操作之后你手上有的珠宝的价值之和的最大值 求执行完 最多k次操作之后 你手上有的珠宝的价值之和的最大值 求执行完最多k次操作之后你手上有的珠宝的价值之和的最大值
思路:
注意到 n 的最大范围只有 50 , k 最大只有 100 注意到n的最大范围只有50,k最大只有100 注意到n的最大范围只有50,k最大只有100
那么可以枚举左边选了多少个 , 右边选了多少个 那么可以枚举左边选了多少个,右边选了多少个 那么可以枚举左边选了多少个,右边选了多少个
如果选的当中有负的价值 , 肯定要把最小的从手中删除 如果选的当中有负的价值,肯定要把最小的从手中删除 如果选的当中有负的价值,肯定要把最小的从手中删除
( 负数越小绝对值越大 ) (负数越小绝对值越大) (负数越小绝对值越大)
前提是还有操作数可以用 前提是还有操作数可以用 前提是还有操作数可以用
如果手上的都是正价值 , 可以不用继续操作 如果手上的都是正价值,可以不用继续操作 如果手上的都是正价值,可以不用继续操作
更新一下当前答案即可 更新一下当前答案即可 更新一下当前答案即可
时间复杂度: O n 3 l o g n On^3logn On3logn
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 2010 , mod = 1e9 + 7 ;int n , k ;
int a[N] ;signed main()
{cin >> n >> k ;fer(i,1,n) sf(a[i]) ;int res = 0 ; // 答案for(int i = 0 ; i <= n ; i ++) // 左边选i个{for(int j = 0 ; j <= n ; j ++) // 右边选j个{if(i + j > k) continue ; // 如果操作数大于k了 就退出multiset<int> q ; // 包含重复元素的set(set内元素从小到大自动排序)int s = 0 ; // 这种选法的最大值// 左边选i个for(int l = 1 ; l <= i ; l ++){q.insert(a[l]) ; s += a[l] ;}// 右边选j个for(int r = n ; r >= n - j + 1 && r > i ; r --){q.insert(a[r]) ;s += a[r] ;}if(i + j < k) // 如果操作数不够k个 , 剩下的操作用来删除 {int d = k - (i + j) ; // 可以用来删除的操作数while(d) // 只要 d > 0 就可以删{if(*q.begin() >= 0) break ; // 如果最小的元素大于等于0 说明不用删除if(*q.begin() < 0) // 反之删除{s -= *q.begin() ; q.erase(q.begin()) ;}d -- ; }}res = max(res,s) ; // 更新答案}}cout << res << "\n" ;return 0;
}
E - 道路工程
题意:
有一条从西到东的无限长的街道,我们认为这是一条无限长的数轴 有一条从西到东的无限长的街道,我们认为这是一条无限长的数轴 有一条从西到东的无限长的街道,我们认为这是一条无限长的数轴
这条街上计划进行 N 项道路工程。 这条街上计划进行N项道路工程。 这条街上计划进行N项道路工程。
第 i 个道路工程在坐标 X i 处阻段该点阻挡的时间从 S i − 0.5 到 T i − 0.5 第i个道路工程在坐标Xi处阻段该点 阻挡的时间从Si - 0.5 到 Ti - 0.5 第i个道路工程在坐标Xi处阻段该点阻挡的时间从Si−0.5到Ti−0.5
也就是说在 S i − 0.5 到 T i − 0.5 这段时间内 X i 这个点不能通过 也就是说在 Si - 0.5 到 Ti - 0.5 这段时间内 Xi 这个点不能通过 也就是说在Si−0.5到Ti−0.5这段时间内Xi这个点不能通过
第 i 个人将在时间 D i 开始从坐标 0 , 不断以速度 1 个单位每秒正向行走,到达阻段点时停止行走。 第i个人将在时间Di开始从坐标0 , 不断以速度1个单位每秒正向行走,到达阻段点时停止行走。 第i个人将在时间Di开始从坐标0,不断以速度1个单位每秒正向行走,到达阻段点时停止行走。
求出每个人将要走的距离 求出每个人将要走的距离 求出每个人将要走的距离
如果这个人走的过程中不会遇到阻断点 如果这个人走的过程中不会遇到阻断点 如果这个人走的过程中不会遇到阻断点
则输出 − 1 则输出-1 则输出−1
1 < = N , Q < = 2 e 5 ( 200000 ) 1 <= N , Q <= 2e5 (200000) 1<=N,Q<=2e5(200000)
0 < = S i < T i < = 1 e 9 ( 1000000000 ) 0 <= Si < Ti <= 1e9 (1000000000) 0<=Si<Ti<=1e9(1000000000)
1 < = X i < = 1 e 9 1 <= Xi <= 1e9 1<=Xi<=1e9
0 < = D 1 < D 2 < D 3 < . . . . . . . < D q < = 1 e 9 0 <= D1 < D2 < D3 < ....... < Dq <= 1e9 0<=D1<D2<D3<.......<Dq<=1e9
思路:
其实我们可以发现每个人到达每个点的时间是动态的 其实我们可以发现每个人到达每个点的时间是动态的 其实我们可以发现每个人到达每个点的时间是动态的
枚举每个人去求行走距离显然不行 枚举每个人去求行走距离显然不行 枚举每个人去求行走距离显然不行
但是每个阻挡点的阻挡时间是不变的 但是每个阻挡点的阻挡时间是不变的 但是每个阻挡点的阻挡时间是不变的
考虑从这上面做 考虑从这上面做 考虑从这上面做
在 S i − 0.5 到 T i − 0.5 这段时间内 X i 这个点不能通过 在 Si - 0.5 到 Ti - 0.5 这段时间内 Xi 这个点不能通过 在Si−0.5到Ti−0.5这段时间内Xi这个点不能通过
等价于在区间 [ S i , T i − 1 ] 这段时间之内这个人必须不能经过这个点 等价于在区间[Si,Ti-1]这段时间之内这个人必须不能经过这个点 等价于在区间[Si,Ti−1]这段时间之内这个人必须不能经过这个点
否则答案就是 X i 否则答案就是Xi 否则答案就是Xi
假设这个人从 0 号点的出发时间为 k 假设这个人从0号点的出发时间为k 假设这个人从0号点的出发时间为k
他到达 X i 这个点的时间为 X i + k 他到达Xi这个点的时间为Xi+k 他到达Xi这个点的时间为Xi+k
如果 S i < = X i + k < = T i − 1 如果Si <= Xi + k <= Ti-1 如果Si<=Xi+k<=Ti−1
说明这个人最远只能到达 X i 这个点 说明这个人最远只能到达Xi这个点 说明这个人最远只能到达Xi这个点
把式子变形一下即为 把式子变形一下即为 把式子变形一下即为
S i − X i < = k < = T i − X i − 1 Si - Xi <= k <= Ti - Xi -1 Si−Xi<=k<=Ti−Xi−1
所以我们可以预处理所有的 [ S i − X i , T i − X i − 1 ] 这个区间 所以我们可以预处理所有的[Si-Xi,Ti-Xi-1]这个区间 所以我们可以预处理所有的[Si−Xi,Ti−Xi−1]这个区间
这个区间的权值为 X i 这个区间的权值为Xi 这个区间的权值为Xi
所以题目变成了 所以题目变成了 所以题目变成了
给你 n 个区间 , 每个区间都有一个权值 给你n个区间,每个区间都有一个权值 给你n个区间,每个区间都有一个权值
在给你 q 个询问 在给你q个询问 在给你q个询问
每个询问给你一个数 每个询问给你一个数 每个询问给你一个数
求这个数所在区间的最小权值 求这个数所在区间的最小权值 求这个数所在区间的最小权值
其实很明显是一个区间修改 + 单点最小值查询 其实很明显是一个区间修改+单点最小值查询 其实很明显是一个区间修改+单点最小值查询
可以裸线段树 / 平衡树 + 离散化 O n l o g n 可以裸线段树/平衡树+离散化Onlogn 可以裸线段树/平衡树+离散化Onlogn
代码长度可能至少 100 行这里不太推荐 代码长度可能至少100行 这里不太推荐 代码长度可能至少100行这里不太推荐
这里推荐另外一种方法 这里推荐另外一种方法 这里推荐另外一种方法
二分 + s e t 动态维护 二分+set动态维护 二分+set动态维护
对区间按照权值大小从小到大排序 对区间按照权值大小从小到大排序 对区间按照权值大小从小到大排序
二分找到这个区间所有的人 二分找到这个区间所有的人 二分找到这个区间所有的人
更新这些人的答案在把这些人删掉 更新这些人的答案在把这些人删掉 更新这些人的答案在把这些人删掉
每个人最多只被遍历一次 每个人最多只被遍历一次 每个人最多只被遍历一次
可以做到时间复杂度为 n l o g n 可以做到时间复杂度为nlogn 可以做到时间复杂度为nlogn
时间复杂度: O n l o g n Onlogn Onlogn
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e6 + 10 , M = 2010 , mod = 1e9 + 7 ;int n , t ; // n个阻断工程和t个人
struct ai{int l , r , x ;
}q[N] ; // 保存区间及其权值
set<pll> se ; // set里面放2个数 {到达0号点的的时间,编号}
int ans[N] ; // 答案数组bool cmp(ai a , ai b)
{return a.x < b.x ;
}
// 按照Xi的权值大小从小到大排序signed main()
{cin >> n >> t ;fer(i,1,n){int l , r , x ;sf(l) , sf(r) , sf(x) ;q[i] = {l-x,r-x-1,x} ; }sort(q + 1 , q + 1 + n , cmp) ;fer(i,1,t){int x ;sf(x) ;se.insert({x,i}) ; // {这个人到达0号点的的时间,编号}}fer(i,1,t) ans[i] = -1 ;fer(i,1,n){int l = q[i].l , r = q[i].r , x = q[i].x ;while(se.size()) // 只要set里面还有数{auto it = se.lower_bound({l,-2e9}) ;// 找到第一个大于等于l这个数if(it == se.end()) break ; // 如果它不存在 就退出// stl的所有容器end() 指向一个不存在的数 自动处理边界问题if(it -> first > r) break ; // 如果这个数不在[l,r]这个区间之内,也退出 ans[it -> second] = x ; // it -> second 表示这个人的编号se.erase(it) ; // 删除// set的操作基于平衡树 单次操作logn}}// 边遍历边删除 每个数只被遍历一次 故时间复杂度为nlognfer(i,1,t) cout << ans[i] << "\n" ;return 0;
}
F - 蛙跳
题意:
有一只青蛙,有 0 , 1 , ⋯ , N − 1 个荷叶。每个荷叶上有权值 s i 。 有一只青蛙,有0,1,⋯,N−1个荷叶。每个荷叶上有权值si。 有一只青蛙,有0,1,⋯,N−1个荷叶。每个荷叶上有权值si。
你现在在 0 这个荷叶上 你现在在0这个荷叶上 你现在在0这个荷叶上
操作 1 操作1 操作1
选定任意的 2 个正整数 A , B ,初始分数为 0 。 选定任意的2个正整数A, B,初始分数为0。 选定任意的2个正整数A,B,初始分数为0。
假设当前位置为 x : 假设当前位置为x: 假设当前位置为x:
操作 2 操作2 操作2
你的坐标移动到 x + A 这个位置上,并且 y = x + A 你的坐标移动到 x + A 这个位置上 , 并且y = x + A 你的坐标移动到x+A这个位置上,并且y=x+A
如果 y = N − 1 ,游戏结束。 如果y=N−1,游戏结束。 如果y=N−1,游戏结束。
如果 y ≠ N − 1 ,但是 y 这个荷叶存在,那么分数增加 s i ,并且这片荷叶消失。 如果y≠N−1,但是y这个荷叶存在,那么分数增加si,并且这片荷叶消失。 如果y=N−1,但是y这个荷叶存在,那么分数增加si,并且这片荷叶消失。
如果 y ≠ N − 1 ,但是 y 这个荷叶不存在,那么分数减去 10 的 100 次方,游戏结 如果y≠N−1,但是y这个荷叶不存在,那么分数减去10的100次方,游戏结 如果y=N−1,但是y这个荷叶不存在,那么分数减去10的100次方,游戏结 束。 束。 束。
操作 3 操作3 操作3
你的坐标移动到 x − B 这个位置上,并且 y = x − B 你的坐标移动到 x - B 这个位置上 , 并且y = x - B 你的坐标移动到x−B这个位置上,并且y=x−B
如果 y = N − 1 ,游戏结束。 如果y=N−1,游戏结束。 如果y=N−1,游戏结束。
如果 y ≠ N − 1 ,但是 y 这个荷叶存在,那么分数增加 s i ,并且这片荷叶消失。 如果y≠N−1,但是y这个荷叶存在,那么分数增加si,并且这片荷叶消失。 如果y=N−1,但是y这个荷叶存在,那么分数增加si,并且这片荷叶消失。
如果 y ≠ N − 1 ,但是 y 这个荷叶不存在,那么分数减去 10 的 100 次方,游戏结 如果y≠N−1,但是y这个荷叶不存在,那么分数减去10的100次方,游戏结 如果y=N−1,但是y这个荷叶不存在,那么分数减去10的100次方,游戏结 束。 束。 束。
然后不断重复 2 和 3 操作 然后不断重复2和3操作 然后不断重复2和3操作
问选定最优的 A 、 B 的情况下,得到的最高分数为多少? 问选定最优的A、B的情况下,得到的最高分数为多少? 问选定最优的A、B的情况下,得到的最高分数为多少?
1 < = n < = 1 e 5 ( 100000 ) 1 <= n <= 1e5(100000) 1<=n<=1e5(100000)
− 1 e 9 < = s i < = 1 e 9 ( 1 后面 9 个 0 ) -1e9 <= si <= 1e9 (1后面9个0) −1e9<=si<=1e9(1后面9个0)
s 0 = s n − 1 = 0 s0 = sn-1 = 0 s0=sn−1=0
思路:
考虑,选定了 A 、 B 后,青蛙的行走路线为: 考虑,选定了A、B后,青蛙的行走路线为: 考虑,选定了A、B后,青蛙的行走路线为:
0 , A , A − B , A + ( A − B ) , 2 ( A − B ) , ⋯ , K ( A − B ) , A + K ( A − B ) 0,A,A−B,A+(A−B),2(A−B),⋯,K(A−B),A+K(A−B) 0,A,A−B,A+(A−B),2(A−B),⋯,K(A−B),A+K(A−B)
令 C = A − B : 令C=A−B: 令C=A−B:
0 , A , C , A + C , 2 C , ⋯ , K C , A + K C 0,A,C,A+C,2C,⋯,KC,A+KC 0,A,C,A+C,2C,⋯,KC,A+KC
显然有: A + K C = N − 1 : 显然有:A+KC=N−1: 显然有:A+KC=N−1:
将 A = N − 1 − K C 带入上式 将A = N - 1 - KC带入上式 将A=N−1−KC带入上式
0 , N − 1 − K C , C , N − 1 − ( K − 1 ) C , 2 C , ⋯ , K C , N − 1 0,N−1−KC,C,N−1−(K−1)C,2C,⋯,KC,N−1 0,N−1−KC,C,N−1−(K−1)C,2C,⋯,KC,N−1
那么当 K 、 C 确定的时候,行走路线就已经确定。 那么当K、C确定的时候,行走路线就已经确定。 那么当K、C确定的时候,行走路线就已经确定。
并且有一个限制条件为 K C < N ,那么显然枚举 K 、 C 是 O ( n l o g n ) 的。 并且有一个限制条件为KC
并且我们发现,当我们固定 C ,递增 K 的时候,行走路线的变化是这样的: 并且我们发现,当我们固定C,递增K的时候,行走路线的变化是这样的: 并且我们发现,当我们固定C,递增K的时候,行走路线的变化是这样的:
0 , N − 1 0,N−1 0,N−1
0 , N − 1 − C , C , N − 1 0,N−1−C,C,N−1 0,N−1−C,C,N−1
0 , N − 1 − 2 C , C , n − 1 − C , 2 C , N − 1 0,N−1−2C,C,n−1−C,2C,N−1 0,N−1−2C,C,n−1−C,2C,N−1
每次增加的是 N − 1 − K C 和 K C ,这两个点,只需要加上就好了 每次增加的是N−1−KC和KC,这两个点,只需要加上就好了 每次增加的是N−1−KC和KC,这两个点,只需要加上就好了
并且要注意判断是否走到重复的点上了。 并且要注意判断是否走到重复的点上了。 并且要注意判断是否走到重复的点上了。
时间复杂度: O n l o g n Onlogn Onlogn
#include
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define der(i,a,b) for(re i = a ; i >= b ; -- i)
#define de(x) cout << x << "\n"
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int>
#define re register int
#define int long long
#define pb push_back
#define y second
#define x first
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f ;
const int N = 1e5 + 10 , M = 2010 , mod = 1e9 + 7 ;int n ;
int s[N] ;
int used[N] ;signed main()
{cin >> n ;fer(i,0,n-1) sf(s[i]) ;int res = 0 ;for(int c = 1 ; c <= n ; c ++){int ans = 0 ;for(int k = 1 ; k * c < n ; k ++){int a = k * c;int b = n - 1 - k * c;int A = b, B = b - c;if (A <= 0 || B <= 0) break; if (a < 0 || a >= n || b < 0 || b >= n || a == b) break; if (used[a] == c || used[b] == c) {break;}used[a] = c;used[b] = c;ans += s[a];ans += s[b];res = max(res, ans);}}cout << res << "\n" ;return 0;
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
