The 2025 ICPC Taiwan Technology University Programming Contest (TUPC)

程式碼在 Github 上,歡迎自由取用

Problem B
Diamond Attacker

Input Format

r1 u1 v1 d1
r2 u2 v2 d2
r3 u3 v3 d3
...
  • r: the order of the diamond attacker
  • u: target x-coordinate
  • v: target y-coordinate
  • d: modulo divisor

Techinical Specification

  • 0u22025
  • 0|v|22025
  • 0r7
  • 1d109

Sample Input

1 0 0 9999
1 1 3 9999
2 3 1 9999

Sample Output

1
0
2

Solution

Modulo Operation

這題需要對模除運算有一定的了解,包含以下恆等式:

(a+b)modd=[(amodd)+(bmodd)]modd

abmodd=(amodd)(bmodd)modd

推廣到矩陣:

ABmodd=(Amodd)(Bmodd)modd

Detailed Derivation

A=[aij],for i=1m,j=1nB=[bij],for i=1n,j=1pC=[cij]=ABCmodd=[cijmodd],for i=1m,j=1p=[(k=1naikbkj)modd]=[(k=1n(aikbkjmodd))modd]=[(k=1n[(aikmodd)(bkjmodd)modd])modd]=(Amodd)(Bmodd)modd

Abmodd=(Amodd)bmodd

Detailed Derivation

Abmodd=AAAbmodd=(Amodd)(Amodd)(Amodd)bmodd=(Amodd)bmodd

後續說明為求方便,省略了一些

modd,但利用模除運算的分配律性質,只要在每次運算後加上取模即可算出答案。

Impossible Condition

首先排除不可能到達的情況,下圖為所有可能到達的地點,觀察可發現,當

u<|v|
(uv)mod20
則不可能到達,此時直接輸出
0

Highest Path

題目要求找到所有路徑中,包含最大

y 座標的路徑數量。觀察過後可以發現,不論
r
為何、亦不論每步走幾格,此路徑必定為先向右上,再向右下,以下方為例,若目標為
(9,3)
,◉ 為起點與終點, ● 代表選擇路徑,○ 代表其他可能路徑:

則包含最高

y 座標的路徑僅有一條,因此可將問題拆分為起點 -> 最高點,與最高點 -> 終點兩段。第一段路徑可寫成
(i,i)
,第二段則可寫成
(uj,v+j)
,計算得到交點
(u+v2,u+v2)

Number of Highest Path

dp[i] 為可能的路徑數量,其中
i
為與終點的距離,則:

  • 第一段路為
    (0,0)
    (u+v2,u+v2)
    ,可能的路徑數為
    dp[u+v2]
  • 第二段路為
    (u+v2,u+v2)
    (u,v)
    ,可能的路徑數為
    dp[uv2]

則最終答案,總路徑數為:

dp[u+v2]dp[uv2]

Step Size

題目對於一步可以走到的位置定義如下:

(x+(i+j),y+(ji)),0<=i,j<2r

由此可知,前進一步的最長距離為

2r1,因此定義步長:

s=2r1

代表可以選擇前進

1,2,3,,2r,2r1

Dynamic Programming Function

假設步長

s=3,則可以推導出以下規律:

e.g.s=3dp[0]=1dp[1]=dp[0]=1dp[2]=dp[1]+dp[0]=2dp[3]=dp[2]+dp[1]+dp[0]=4dp[4]=dp[3]+dp[2]+dp[1]=7dp[5]=dp[4]+dp[3]+dp[2]=13

舉例來說,

dp[2] 可以理解為「走到
2
的路徑數
=
走到
1
的路徑數
+
走到
0
的路徑數」。
以 piecewise function 表示:

dp[i]={0i<01i=0j=1sdp[ij]i>0

Matrix Representation of Dynamic Programming Function

對於

i>0,可以以一個
s×s
矩陣與
s×1
陣列相乘表示其關係:

[dp[is+1]dp[is+2]dp[is+3]dp[is+4]dp[i2]dp[i1]dp[i]]=[0100000001000000010000000000000001000000011111111][dp[is]dp[is+1]dp[is+2]dp[is+3]dp[i3]dp[i2]dp[i1]]

將上述等式表示為:

xi=Axi1

利用其遞迴關係,可以一路追溯到初始狀態

x0:

xi=AAxi2=A2xi2=A3xi3==Aix0

其中:

x0=[dp[s+1]dp[s+2]dp[s+3]dp[s+4]dp[2]dp[1]dp[0]]=[0000001]

因此,對於

i>0,只要取得
xi
的最後一項即可得到答案。

Fast Matrix Exponentiation

題目定義

u
|v|
最大可達
22025
,而
i
的大小與其相關,因此需要使用快速冪技巧計算
Ai
。另外,因為輸入太大,無法以數字類型(64-bit int)儲存,因此需以十進制陣列儲存,計算快速冪時也以十進制為主。

快速冪的核心概念如下,假設要計算

A12345 :

A12345=(A1234)10A5=((A123)10A4)10A5=(((A12)10A3)10A4)10A5=((((A1)10A2)10A3)10A4)10A5

以代數表示:

Ab=A10b+r=(Ab)10Ar,b÷10=br

因此,只要以左到右遍歷

b 的每一位數,重複10次方、乘
Ar
兩個動作就能完成矩陣快速冪,pseudocode 如下:

function matPow(A, b): // result = A^b
    result = idenetity matrix with the same size as A
    for digit in b: // decimal digits, from left to right
        result = (result ** 10) * (A ** digit)
    return result

Acceleration

上述的2個步驟皆有可加速的地方。若要計算矩陣的 10 次方,可以將其拆分成以下運算:

A10=A2((A2)2)2

如此只需要 4 次矩陣乘法即可完成計算。

計算

Ar 時,考慮到
r{0,1,,9}
,可以預先建立 Lookup Table Matrix lut[10],需要時直接取用 lut[r],大幅減少計算時間。

最終演算法複複雜度為

O(r3log(u))
r3
來自矩陣乘法,
log(u)
則是因為要分別遍歷
u
的所有位數。