# APCS實作題2019年6月第3題:互補團隊 > 日期:2024年8月29日 > 作者:王一哲 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=e288) ## 題目 ### 問題描述 每一個字元代表一名角色,由於這個遊戲的角色包括教師只有38位,所以他們用'A' \~ 'Z'和'a' \~ 'l'來表示第 1 到第 38 位角色。但是由於他們太喜歡這些角色了,所以每個字母可以出現在字串中很多次,例如 "AAAAAA" 和 "A" 其實是同一組 CP。 最近他們找到了新的消遣,他們觀察出一種叫做CP互補的特殊現象,只要兩個CP之間沒有重複的角色且兩個CP的角色集合包含全部角色,我們就說CP之間互補。現在他們開始討論他們喜歡的CP,且他們想要知道這些CP中有那些是互補的,於是他們拜託你寫個程式幫他們數。 <br /> ### 輸入說明 單筆輸入 第一行有兩個數字 $m, n$ ,分別代表這次他們這次只討論前 $m$ 個角色和CP數量。 接著有 $n$ 行,每行有一個字串代表一組 CP,字串長度在 1 ~ 1000 之間。 配分 - 5%:$m = 2,~ n \leq 1000$ - 25%:$m \leq 26,~ n \leq 10000$ - 75%:$m \leq 26,~ n \leq 100000$ - 100%:$m \leq 38,~ n \leq 500000$ <br /> ### 輸出說明 輸出互補CP的對數 <br /> ### 範例輸入 ``` 3 4 ABB AB C CC ``` ### 範例輸出 ``` 4 ``` <br /> ## Python 程式碼 將字元轉成2進位數字,使用位元運算。第5筆測資開始超時。 ```python= from collections import defaultdict def convert(c): # 將字元 c 轉成編號,A 為 0,Z 為 25,a 為 26 return ord(c)-ord('A') if c<='Z' else ord(c)-ord('a')+26 m, n = map(int, input().split()) # 讀取角色數量 m,組數 n table = defaultdict(int) # 字串對應的數量 for _ in range(n): # 讀取 n 筆資料 s = input() # 輸入字串 tmp = 0 # 暫存編號 for c in s: tmp |= 1<<abs(convert(c)) # 用 or 運算及 convert 將字串轉成編號 table[tmp] += 1 # 字串對應的數量加 1 ans = 0 # 答案預設為 0 for sn, num in table.items(): # 由 table 依序取出字串編號及數量 tmp = ((1<<m)-1)^(sn) # 先産生 m 個 1,再用 XOR 運算找出 sn 的補數 if tmp in table: ans += num*table[tmp] # 如果 table 之中有 tmp,ans 加上 sn 及其補數數量乘積 print(ans//2) # 答案要除以2 ``` <br /><br /> ## C++ 程式碼 將字元轉成2進位數字,使用位元運算。其中第19、24行的 1 要用 (LL) 轉型成 long long,否則會得到錯的數值。費時最久約為 0.4 s,使用記憶體最多約為 27.9 MB,通過測試。 ```cpp= #include <iostream> #include <string> #include <unordered_map> typedef long long LL; using namespace std; LL convert(char c) { // 將字元 c 轉成編號,A 為 0,Z 為 25,a 為 26 return c<='Z' ? c-'A' : c-'a'+26; } int main() { ios::sync_with_stdio(0); cin.tie(0); int m, n; cin >> m >> n; // 讀取角色數量 m,組數 n unordered_map<LL, LL> table; // 字串對應的數量 string s; for(int i=0; i<n; i++) { // 讀取 n 筆資料 cin >> s; // 輸入字串 LL tmp = 0; // 暫存編號 for(char c : s) tmp |= (LL)1<<(convert(c)); // 用 or 運算及 convert 將字串轉成編號 table[tmp]++; // 字串對應的數量加 1 } LL ans = 0; // 答案預設為 0 for(auto it=table.begin(); it!=table.end(); it++) { // 由 table 依序取出字串編號及數量 LL tmp = (((LL)1<<m)-1)^(it->first); // 先産生 m 個 1,再用 XOR 運算找出 sn 的補數 if (table.find(tmp) != table.end()) ans += (it->second)*table[tmp]; // 如果 table 之中有 tmp,ans 加上 sn 及其補數數量乘積 } cout << ans/2 << "\n"; // 答案要除以2 return 0; } ``` <br /> 修改一點寫法。費時最久約為 0.4 s,使用記憶體最多約為 27.9 MB,通過測試。 ```cpp= #include <iostream> #include <string> #include <unordered_map> typedef long long LL; using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); LL m, n; cin >> m >> n; LL comp = ((LL)1 << m) - 1; unordered_map<LL, LL> teams; string s; for(int i=0; i<n; i++) { cin >> s; LL code = 0; for(char c : s) { if (c <= 'Z') code |= (LL)1 << (c - 'A'); else code |= (LL)1 << (c - 'a' + 26); } teams[code]++; } LL ans = 0; for(auto it = teams.begin(); it != teams.end(); it++) { LL target = comp^(*it).first; if (teams.find(target) != teams.end()) ans += (*it).second * teams[target]; } cout << ans/2 << "\n"; return 0; } ``` <br /><br /> ## 參考資料 吳邦一教授的 APCS 解題影片〈[PyAp45 Python APCS 題解:互補團隊(APCS201906_3), AP325 Q-2-7., 中一中 TCFSH CIRC 編號d016.](https://youtu.be/5XSydGMi2HA?si=LxI0Y-Mn4yN2oZK1)〉 --- ###### tags:`APCS`、`Python`、`C++`