読者です 読者をやめる 読者になる 読者になる

ferinの競プロ帳

競プロについてのメモ

ABC021を解いてみた

競技プログラミング ABC

A問題

 nが8より大きいか、4より大きいか…と貪欲的に判定していきました。
A問題にしては難しいなーと思ったら数字の重複可能なことを見逃してました…

n = int(input())

flag = [0 for _ in range(4)]
count = 0

if n >= 8:
    flag[3] = 1
    n -= 8
    count += 1
if n >= 4:
    flag[2] = 1
    n -= 4
    count += 1
if n >= 2:
    flag[1] = 1
    n -= 2
    count += 1
if n >= 1:
    flag[0] = 1
    n -= 1
    count += 1

print(count)

for i in range(4):
    if flag[i] == 1:
        print(2 ** i)

B問題

 入力例を見て考えると、同じ街を通っていれば最短経路ではないといえそうです。
そこで始点、終点、経由した街に重複がなければYESを出力すれば良さそうです。

import sys

n = int(input())
a, b = map(int, input().split())
k = int(input())
p = [int(i) for i in input().split()]

p.append(a)
p.append(b)

# 重複確認
for i in range(len(p)):
    for j in range(len(p)):
        if i != j and p[i] == p[j]:
            print("NO")
            sys.exit()

print("YES")

C問題

 ワーシャルフロイド法を用いて始点から全ての点への最短経路を求め、始点からメモ化再帰で最短経路を数えたつもりだったんですがREが出てしまいました… 手元の環境では実行時エラーが出せず、直せません…

D問題

 深さ優先探索→メモ化再帰動的計画法アルゴリズムを変えていき計算量O(nk)で部分点解法になりました。

# メモ化再帰
n, k = map(int, input().split())

dp = [[0 for i in range(n)] for j in range(k)]
mo = 1000007

def dfs(i, num):
    count = 0
    if(dp[i-1][num-1] != 0):
        return dp[i-1][num-1]
    # print("i:{0} num:{1}".format(i, num))

    if(i == k):
        return 1

    for j in range(n-num+1):
        count = (count + (dfs(i+1, num+j) % mo)) % mo

    dp[i-1][num-1] = count % mo
    print("dp[{0}][{1}] = {2}".format(i-1, num-1, dp[i-1][num-1]))
    return count % mo

ans = 0
for i in range(1, n+1):
    ans = (ans + dfs(1, i)) % mo
print(ans)
# 動的計画法
n = int(input())
k = int(input())

dp = [[0 for i in range(n)] for j in range(k)]
for i in range(n):
    dp[k-1][i] = 1

mo = 1000000007

for i in range(1, k):
    _i = k-i-1
    for j in range(n):
        _j = n-j-1
        if(_j == n-1):
            dp[_i][_j] = dp[_i+1][_j] % mo
        else:
            dp[_i][_j] = (dp[_i+1][_j] + dp[_i][_j+1]) % mo

ans = 0
for i in range(n):
    ans = (ans + dp[0][i]) % mo

print(ans)

次に解説を読んで重複組み合わせの考え方を用いてO(NlogP)で実装したつもりなんですがTLEでした…

n = int(input())
k = int(input())

mod = 1000000007

# 二分累乗法
def pow(x, n, mo):
    if n == 0: return 1
    if n == 1: return x

    if (n%2)==0:
        return ((pow(x, n/2, mod) % mo) ** 2) % mo
    else:
        return (x * (((pow(x, int(n/2), mod) % mo) ** 2) % mo)) % mo

fact = [0 for i in range(n+k)]
ifact = [0 for i in range(n+k)]
fact[0] = 1
ifact[0] = 1

for i in range(1, n+k):
    fact[i] = fact[i-1] * i % mod
    ifact[i] = pow(fact[i], mod-2, mod)

ret = (fact[n+k-1]*ifact[k]%mod)*ifact[n-1]%mod
print(ret)