畢竟只是Qualification Round,
沒有名次、時間壓力,只要分數達到20分就晉級了,
我先前就自己下了一個目標:
要只使用python完成qualification round!
結果還算順利,自己比賽期間解了前三題,得到66分 時間15:44:11(含penalty) 1567名的成績。
輸入方式是依序輸入害羞程度為\(0,1,2,...\)的人數,十分有利於處理。
策略:從害羞程度小到大掃過去,如果已經站起來的人數不夠,就加自己邀請來的人
時間複雜度:\(O(S_{max})\)
AC 的python code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | NumberOfTestCases = int( input() ) for NumTestCases in range(1,NumberOfTestCases+1): print('Case #',NumTestCases,': ',sep='',end='') Smax,str=input().split() Smax=int(Smax) ans = 0 sum = 0 for i in range(0,Smax+1): if sum < i: ans += 1 sum += 1 sum += int(str[i]) print(ans) |
1.不做事,所有有派的桌子上會減少一個派
2.把某張桌子上的一些派移到某張桌子上,這一分鐘內所有桌子(除了被你調整)的派都不會減少
請以最佳策略在最少時間內讓所有桌子的派都被吃光。
我一開始想到一個策略:
用priority_queue維護所有桌子的派數量,每次把最多的一桌分兩半並更新答案
不過這是錯誤的!9
因為是不夠熟悉的python,所以我還特地上網查priority_queue的用法,然後就這樣WA了一遍。
特別紀念這份code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from heapq import heappush from heapq import heappop from heapq import heapify NumberOfTestCases = int( input() ) for NumTestCases in range(1,NumberOfTestCases+1): D = int(input()) P = input().split() for i in range(0,D): P[i] = -int(P[i]) heapify(P) ans = -P[0] t = 0 while t < ans: t += 1 tmp = heappop(P) th = tmp//2 heappush(P,th) heappush(P,tmp-th) if -P[0]+t < ans: ans = -P[0]+t print('Case #',NumTestCases,': ',sep='',end='') print(ans) |
WA了之後,我就拿剛剛下載的input慢慢測試、研究,
所幸當我發現錯誤的同時,我就想到一個比這好寫很多的正解了!
枚舉最多的一桌有幾個派,計算分派要花幾分鐘+吃派的時間,取最小
正確性也更加明確!時間複雜度:\(O(D\times P_{max})\)
原本想說\(1000\times1000\)很小,所以code沒有特地取\(P_{max}\)
結果清楚感受到python效率不佳,large估計跑了半分鐘@@
AC 的python code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | NumberOfTestCases = int( input() ) for NumTestCases in range(1,NumberOfTestCases+1): D = int(input()) P = input().split() for i in range(0,D): P[i] = int(P[i]) ans = 1000 for i in range(1,1001): count = i for x in P: count += (x-1)//i if count < ans: ans = count print('Case #',NumTestCases,': ',sep='',end='') print(ans) |
不過畢竟是題解,簡要的提點一下:給一串四元數,問是否能切成三段,使得三段的乘積分別是\(i,j,k\)
其中輸入四元數的方式是給長度不超過\(10^4\)的字串\(S\)及正整數\(X(X\leq10^{12})\),表示該四元數為\(S\)重複\(X\)次
要發現兩個關鍵點:
1. \(i,j,k,1\)搭配負號共\(8\)個元素對乘法構成一個群(group)
2. 任何四元數的四次方都是\(1\)
基於1.,對於small,只要從前面開始找一段為\(i\)的、接續找一段為\(j\)的、看剩的是不是\(k\)。
至於large,有一種作法是把\(\geq12\)的\(X\)變成\([8,12)\)中與\(X\)模\(4\)同餘的數,然後套用上面的方法。
不過我不是這樣做!
我的想法是:從前面找一段為\(i\)、從後面找一段為\(k\),然後判斷中間是不是\(j\),針對中間段優化避免\(X\)很大的問題。
不過寫完之後很快就WA了QQ
所以我把上述只能處理small的作法寫出來幫助我debug。
只能過small的code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | def mul(a,b): minus=0 if a[0]=='-': minus+=1 x=a[1] else: x=a[0] if b[0]=='-': minus+=1 y=b[1] else: y=b[0] if x=='1': if minus%2==1: y='-'+y return y if y=='1': if minus%2==1: x='-'+x return x if x==y: if minus%2==1: return '1' return '-1' use='ijkij' for i in range(0,3): if x==use[i] and y==use[i+1]: if minus%2==1: return '-'+use[i+2] return use[i+2] if x==use[i+1] and y==use[i]: if minus%2==0: return '-'+use[i+2] return use[i+2] NumberOfTestCases = int( input() ) for NumTestCases in range(1,NumberOfTestCases+1): print('Case #',NumTestCases,': ',sep='',end='') L,X=map(int,input().split()) ori=input() s=ori*X sum='1' u=0 end=len(s) while u<end: sum=mul(sum,s[u]) u+=1 if(sum=='i'): break if sum!='i': print('NO') continue sum='1' while u<end: sum=mul(sum,s[u]) u+=1 if(sum=='j'): break if sum!='j': print('NO') continue sum='1' while u<end: sum=mul(sum,s[u]) u+=1 if sum!='k': print('NO') else: print("YES") |
AC 的python code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | def mul(a,b): minus=0 if a[0]=='-': minus+=1 x=a[1] else: x=a[0] if b[0]=='-': minus+=1 y=b[1] else: y=b[0] if x=='1': if minus%2==1: y='-'+y return y if y=='1': if minus%2==1: x='-'+x return x if x==y: if minus%2==1: return '1' return '-1' use='ijkij' for i in range(0,3): if x==use[i] and y==use[i+1]: if minus%2==1: return '-'+use[i+2] return use[i+2] if x==use[i+1] and y==use[i]: if minus%2==0: return '-'+use[i+2] return use[i+2] NumberOfTestCases = int( input() ) for NumTestCases in range(1,NumberOfTestCases+1): print('Case #',NumTestCases,': ',sep='',end='') L,X=map(int,input().split()) ori=input() sum='1' for x in ori: sum = mul(sum,x) tmp=ori*4 end=len(tmp) Iuse=0 r='1' while Iuse<end: r=mul(r,tmp[Iuse]) if r=='i': break Iuse+=1 Kuse=end-1 r='1' while Kuse>=0: r=mul(tmp[Kuse],r) if r=='k': break Kuse-=1 if Iuse>=end or Kuse<0: print('NO') continue Ilen=Iuse+1 Klen=end-Kuse if Ilen+Klen>=L*X: print('NO') continue result = '1' if Ilen%L>0: for i in range(Ilen%L,L): result=mul(result,ori[i]) mid = L*X - ((Ilen-1)//L+1)*L - ((Klen-1)//L+1)*L mid = mid//L mid = mid%4 for i in range(0,mid): result=mul(result,sum) for i in range(0,Kuse%L): result=mul(result,ori[i]) if result=='j': print('YES') else: print('NO') |
賽後與其他人討論時才發現,他是一個群(group),所以他當然有反元素啊!
因此,中間那段根本不用那麼麻煩,只要「前面有\(i\)」、「後面有\(k\)」、「中間段長度大於\(0\)」、「全部是\(-1\)」就足夠了!
所以那段寫得最累還產生bug的地方根本白寫了OAO
賽後聽說大部分人的作法就是硬爆,然後判斷一大堆case就結束了(想當然耳,很容易漏一兩個就炸了)。
也有少部分人認真的寫程式計算。
這回,一開始很順的開了pA,然後一口氣讀完所有題目,就跑去做其他事,有空時想作法、想到就寫。
第三年參加GCJ,感覺今年Qualification最難,不過往年沒有這麼認真寫,而且都用C++
前年,還在資芽,學程式不超過一年的我,在Round1被電爆了。
去年,在TOI,通過Round1,最終Round2衣服戰仍然戰敗。
今年,敬請期待!
P.S. GCJid: jo4
沒有留言:
張貼留言