每日一题 | 环形排列问题
昨日问题
每日一题 | QQ群撩妹问题
这道题是codeforces的D题,链接:https://codeforces.com/contest/1395/problem/D
这道题有一定难度,不容易想到解法。我们先来分析一下问题,n天有n句撩妹的话可以选择,但是中间可能会被禁言,禁言了之后不可以发言,但是同样会消耗话术。有可能选择分数大的话术虽然会禁言,但是可以收益更大,也有可能因为禁言导致了会浪费很多话术不能使用。
我们想要简单地贪心获得解好像不太可能,因为我们并不知道禁言多还是禁言少比较有利,假如说我们知道应该禁言的次数,是不是就可以贪心了呢?
我们很容易发现禁言是必须的,因为杀伤力小于m的话术数量是小于等于n的。我们假设杀伤力大于m的骚话有k句,小于m的有l句,首先我们可以得到k + l = n。我们最少需要禁言(n - l + d) / (d+1)次,由于n - l = k,所以是(k + d) / (d+1)次,最多禁言k次。
我们可以遍历禁言的次数,遍历完了之后,到底要禁言多少天呢?其实这是一个波动值,因为如果禁言发生在结尾,可能并不会禁言满d天。比如我们最后一天说骚话,因为已经是最后一天了,所以禁言d天也就不生效了。
禁言x次的话,最少需要多少天呢?也很容易算,最少需要天,也就是把最后一次禁言放在最后一天。这样意味着我们可以填充更多的普通骚话。
想到这里就简单了,我们只需要遍历一下禁言的次数,用贪心的方法算一下每一种禁言方案获得的最大值就可以了。我们只需要保证我们能说话的时候选得都是杀伤力最大的,禁言的时候选的话术并不重要。我们只需要把所有的话术按照是否大于m分成两个部分进行倒排即可。
提供不能AC代码,因为Python性能太差,会超时。
代码语言:javascript复制n, d, m = map(int, input().split(' '))
records = list(map(int, input().split(' ')))
a, b = [], []
for i in records:
if i > m:
a.append(i)
else:
b.append(i)
a = sorted(a, reverse=True)
b = sorted(b, reverse=True)
ret = 0
for i in range((len(a) + d) // (d+1), len(a)+1):
# 禁言i次最少需要消耗l天
l = (i-1) * (d+1) + 1
# 我们选择杀伤力最大的前i句骚话以及n-l个空缺说普通骚话
s = sum(a[:i]) + (0 if l >= n else sum(b[:n-l]))
ret = max(ret, s)
print(ret)今日问题
环形排列问题给定一个n,代表1-n这n个整数,我们知道n个不同的数的排列有种。在排列当中,我们做一个奇怪的操作,对于整数i,我们选取排列当中i左边最大的j,使得,在i和j当中连接一条无向边。再选取排列中i右侧的最小j,使得,同样连一条无向边。
比如[3, 1, 4, 2],我们可以得到的边是(3, 4), (1, 3), (1, 4), (2, 4),我们可以发现可以构成一个环 3 -> 1 -> 4。请问给定一个整数n,要求所有排列当中可以通过这种方式构成环的排列有多少种?
n的范围是10的6次方。
- END -