Codeforces Round #617 (Div. 3)

独自空忆成欢 提交于 2020-02-11 06:35:42

前言

宅在家还是比较无聊的,所以想学着用用python,然后一搜适合python的online judge,发现codeforces居然正好支持,因为python复杂的功能数据结构还不会,所以先试试div3的题

A

http://codeforces.com/contest/1296/problem/A
题目大意:
给你N个数字,并且允许你用其中出现的数字任意覆盖别的数字,问你能否使得所有数字的和为奇数

只要有一个奇数并且有一个偶数,那么就拿偶数覆盖其他所有的数,然后配上这个奇数就可以了
而全部是偶数显然不行,全部是奇数就看n是奇是偶了

a = input()
Case = int(a)

print('Case = {:d}'.format(Case))

for T in range(Case):
    a = input()
    n = int(a)
    a = input()
    b = a.split(' ')
    count1 = 0
    count2 = 0
    for i in range(n):
        if int(b[i]) % 2 == 0:
            count2 += 1
        else:
            count1 += 1
    if count1 == 0:
        print('NO')
    elif count2 == 0:
        if n % 2 == 0:
            print('NO')
        else:
            print('YES')
    else:
        print('YES')

B

http://codeforces.com/contest/1296/problem/B
题目大意:
你拥有N元,而使用10元就能返1元,如果存在任意多任意金额的商品,问你最多能花出去多少钱

如果你有N元,那么花出去N1010\lfloor\frac{N}{10}\rfloor*10的钱肯定是把优惠用满了的,然后把返回来的钱加上剩下的钱,迭代就可以了

a = input()
Case = int(a)

for T in range(Case):
    a = input()
    n = int(a)
    s = n
    while n >= 10:
        t = n // 10
        s += t
        n = n % 10 + t
    print(s)

C

http://codeforces.com/contest/1296/problem/C
题目大意:
有Up,Down,Left,Right四种操作,缩写为U,D,L,R,其中U和D是一对,L和R是一对,给你一个仅包含UDLR的字符串,让你找出满足U的数量=D的数量,L的数量=R的数量的最短的子串

这个题让我想起来了括号序列,但是想了一下还是不太一样,因为如果把U看成左括号,D看成右括号,但是D先出现U再出现也是满足的。
而且类似样例2中的LURD这种({)}也是满足的。
所以不能用传统的栈的思路,而还是从数量入手更好。
记录x[i]为到达i位置时,U,D的数量,y[i]为L,R的数量
遇到U就给x[i] ++,遇到D就给x[i] --, y[i]处理方法类似
然后就会得到一组(x[i], y[i])的数量,如果i时刻和之前的某个(x[j], y[j])相同,说明这一段之间的U,D守恒,L,R也守恒。
然后一想,这不就是回到了简化前的题意,让机器人在地图上走,(x[i], y[i])就是它在i时刻的坐标嘛…
要实现这个用哈希可以完成,将(x, y)简化成x*maxy + y变成一个数字,然后映射对应它在字符串中的位置,不过python里有很方便的字典,也就直接用了

a = input()
Case = int(a)

maxn = 2 * 100010

x = [0] * maxn
y = [0] * maxn
dict = {}
labels = {
    'U':[1, 0],
    'D':[-1, 0],
    'L':[0, -1],
    'R':[0, 1]
}

for T in range(Case):
    dict.clear()
    dict[0] = -1
    a = input()
    n = int(a)
    a = input()
    ansl = -1
    ansr = n
    for i in range(n):
        x[i] = x[i - 1] + labels[a[i]][0]
        y[i] = y[i - 1] + labels[a[i]][1]
        key = x[i] * maxn + y[i]
        if key in dict:
            if (ansr - ansl > i - dict[key]):
                ansl = dict[key]
                ansr = i
        dict[key] = i
    if ansr - ansl > n:
        print('-1')
    else:
        print('{:d} {:d}'.format(ansl + 2, ansr + 1))

D

http://codeforces.com/contest/1296/problem/D
题目大意:
你有一个随从,你的伤害是a,他的伤害是b,你们一起要打n个怪,第i个怪的血量为h[i],每次遇到一个新的怪,你打一次,随从打一次,然后再你打一次,随从打一次,轮流打,如果怪血量降到0或0以下,他就死了,你打死了怪可以加1分,随从打死不能加分,但是你还有一个权力,可以让随从跳过一个回合,但是这个权力只能用k次,问你最多可以得到几分

首先考虑到只要怪兽血线高于a+b,那么肯定不要用权力,直接打就行,所以现在只用考虑所有怪物血都a+b\le a+b的情况,而在这种情况下,怪的所有血都是靠你一个人打才能有分,因为只要随从出手,那么怪必死无疑。
那么对于不同的怪,得到的分都是一分,而想拿到这一分,使用权力的次数是不一样的,假设怪的血量是hi,那么[1, a]使用0次,(a, 2a]使用一次,所以不看a,2a这种能整除的边界情况就是hia\lfloor\frac{h_i}{a}\rfloor次,而对于能整除的还要多减一个1,方便起见,就使用hi1a\lfloor\frac{h_i-1}{a}\rfloor代替所有情况就好了。
然后得到了使用次数之后,从小到大排序,然后贪心打过去就可以了。

str = input()
spl = str.split(' ')

n = int(spl[0])
a = int(spl[1])
b = int(spl[2])
k = int(spl[3])

str = input()
spl = str.split(' ')

h = [0] * n

for i in range(n):
    h[i] = int(spl[i])
    h[i] %= (a + b)
    if(h[i] == 0):
        h[i] = a + b
    h[i] -= 1
    h[i] = h[i] // a

h.sort()
ans = 0

for i in range(n):
    k -= h[i]
    if k < 0:
        break
    ans += 1

print(ans)

E1

http://codeforces.com/contest/1296/problem/E1
题目大意:
给你一个字符串,让你给他排序,要求给每个位置的字母编一个号,只有编号不同的相邻元素才可以交换,问你是否能只编两个号就让原字符串完成排序

因为是交换相邻元素,所以想到冒泡排序,而字符串长度n又很小(n200n\le 200),所以模拟冒泡排序,每一次需要交换时,就通过并查集标记两个交换的元素为不同元素(同关押罪犯:利用每个元素建立一个反点,标记不同即并查集相互连结反点,如果检查到自己和反点在同一个并查集,则出错,否则依次给未编号的集合标记0,给其反点的集合标记1后输出)

def find(x):
    r = x
    while r != f[r]:
        r = f[r]
    while x != r:
        pre = f[x]
        f[x] = r
        x = pre
    return r

a = input()
n = int(a)
a = input()
str = list(a)

id = [0] * n
f = [0] * 2 * n
name = [0] * 2 * n

for i in range(n):
    id[i] = i
for i in range(2 * n):
    f[i] = i
    name[i] = -1

Find = 0
for i in range(n):
    if Find:
        break
    for j in range(n - i - 1):
        if str[j] > str[j + 1]:
            str[j], str[j + 1] = str[j + 1], str[j]
            id[j], id[j + 1] = id[j + 1], id[j]
            fx = find(id[j])
            fnx = find(id[j] + n)
            fy = find(id[j + 1])
            fny = find(id[j + 1] + n)
            if fx == fy or fnx == fny:
                Find = 1
                break
            else:
                f[fx] = fny
                f[fy] = fnx

if Find:
    print('NO')
else:
    print('YES')
    for i in range(n):
        fx = find(i)
        if name[fx] < 0:
            name[fx] = 0
            if(fx >= n):
                name[find(fx - n)] = 1
            else:
                name[find(fx + n)] = 1
        print('{:d}'.format(name[fx]), end = '')

E2

http://codeforces.com/contest/1296/problem/E2
题目大意:
E1的加强版,给你一个字符串,并让你给他编号,只有不同编号的相邻元素才可以交换,问你最少需要几个编号,才能使得原字符串被排好序

可以发现一个元素想要交换到他正确的位置上,需要交换的元素有:在他前面比他大的元素,在他后面比他小的元素
又因为每个元素都要考虑一次,所以考虑后面比他小的元素的时候,就会把他看成在他前面比他大的元素,所以我们可以从前往后考虑,且只考虑在他前面比他大的元素
然后再通过规律发现:
a : 1
ba : 12
cba: 123
dcba : 1234
发现这种递减序列产生的编号最多
其中如果插入别的元素,也并不影响:
dabcabba 1 2 2 2 3 3 3 4
所以当前元素的大小只与前面比他大的元素构成的最长下降序列有关,而这个最长下降序列的长度最多为26
传统下降子序列递推方程:
f[i]f[i]表示以第i个位置结尾的最长下降子序列长度
f[i]=max{f[j]}+1(a[i]<a[j])f[i]=max\{f[j]\}+1 (a[i]<a[j])
那么就可以通过树状数组优化,将f[i]的值存入下标为a[i]的树状数组中,并通过树状数组更新统计最大值,之后查询也这样查就可以了
因为本题中a[i]a[i]范围很小,所以直接枚举找就可以了
所以可以得到优化后的递推方程:
f[i]f[i]表示当前位置以 i 结尾的最长不降子序列的长度
f[i]=max{f[j]}(j>i)f[i] = max\{f[j]\}(j>i)
时间复杂度应该是O(n26)O(n*26)
结果python超时了…C++只跑了46ms
那就还是放个python代码再放个C++代码吧

str = input()
n = int(str)
str = input()
s = [0] * n
for i in range(n):
    s[i] = ord(str[i]) - ord('a')
ans = 0
f = [0] * 26
for i in range(n):
    maxl = 0
    for j in range(s[i] + 1, 26):
        if maxl < f[j]:
            maxl = f[j]
    f[s[i]] = maxl + 1
    s[i] = f[s[i]]
    ans = max(ans, s[i])

print(ans)
for i in range(n):
    print(s[i], end = ' ')
#include<cstdio>

const int maxn = 2 * 100010;

int rec[maxn];
int f[26];
char s[maxn];

int main(){
	int n, ans = 0;
	scanf("%d%s", &n, s);
	for(int i = 0; i < n; i++){
		int maxl = 0;
		s[i] -= 'a';
		for(int j = s[i] + 1; j < 26; j++)
			maxl = maxl > f[j] ? maxl : f[j];
		f[s[i]] = maxl + 1;
		rec[i] = f[s[i]];
		ans = ans > rec[i] ? ans : rec[i];
	}
	printf("%d\n",ans);
	for(int i = 0; i < n; i ++)
		printf("%d ", rec[i]);
	return 0;
}

F

http://codeforces.com/contest/1296/problem/F
题目大意:
给你一颗树,并且给你n个条件(a,b,k),表示a到b的路径上边权最小的边为k,问你是否能够找到满足条件的边权赋值。如果能找到输出每条边的权值。

应该是最近公共祖先+倍增法的应用
每次找到最近公共祖先,然后更新这一段上的最小值信息就可以了。
不过感觉python应该速度上过不了就算了…

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!