結果
問題 |
No.5001 排他的論理和でランニング
|
ユーザー |
|
提出日時 | 2025-04-19 14:26:51 |
言語 | PyPy3 (7.3.15) |
結果 |
AC
|
実行時間 | 1,288 ms / 1,500 ms |
コード長 | 4,734 bytes |
コンパイル時間 | 510 ms |
コンパイル使用メモリ | 82,248 KB |
実行使用メモリ | 102,040 KB |
スコア | 52,270,145 |
最終ジャッジ日時 | 2025-04-19 14:27:06 |
合計ジャッジ時間 | 13,296 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge2 |
純コード判定しない問題か言語 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 50 |
ソースコード
import copy import random from time import perf_counter import argparse import sys import math random.seed(0) class TimeKeeper: def __init__(self): self.start_time = perf_counter() def is_time_over(self, LIMIT): return (perf_counter() - self.start_time) >= LIMIT def time_now(self): return (perf_counter() - self.start_time) class XorSet: def __init__(self, initial): # リスト+辞書で要素集合を管理し、 # 定数時間でランダム選択・追加・削除・XOR 更新を行う self.data = list(initial) self.pos = {v:i for i,v in enumerate(self.data)} self.current_xor = 0 for v in self.data: self.current_xor ^= v def __len__(self): return len(self.data) def random_element(self): idx = random.randrange(len(self.data)) return self.data[idx] def remove(self, v): idx = self.pos[v] last = self.data[-1] # スワップして末尾を pop self.data[idx] = last self.pos[last] = idx self.data.pop() del self.pos[v] # XOR 更新 self.current_xor ^= v def add(self, v): self.pos[v] = len(self.data) self.data.append(v) # XOR 更新 self.current_xor ^= v def hill_climbing_xor(values, M, tk, LIMIT): # 初期分割 random.shuffle(values) Sa = XorSet(values[:M]) Sb = XorSet(values[M:]) best_xor = Sa.current_xor loop = 0 while True: loop += 1 if loop%100==0 and tk.is_time_over(LIMIT): break # 近傍:A から a_val, Sb から b_val をランダムに選んで交換を試す a_val = Sa.random_element() b_val = Sb.random_element() # 交換後の Sa の XOR を定数時間で計算 new_xor = Sa.current_xor ^ a_val ^ b_val # 改善があれば実際にスワップ if new_xor > Sa.current_xor: Sa.remove(a_val) Sb.remove(b_val) Sa.add(b_val) Sb.add(a_val) best_xor = new_xor print(f"best: {loop=} {best_xor=}", file=sys.stderr) return Sa, Sb, best_xor def hill_climbing_xor2(values, M, K, xor_init, tk, LIMIT, perfect=2**20-1): # K個少ない M = M - K # 初期分割 random.shuffle(values) Sa = XorSet(values[:M]) Sb = XorSet(values[M:]) Sa.current_xor ^= xor_init best_xor = Sa.current_xor loop = 0 while True: loop += 1 if loop%100==0 and tk.is_time_over(LIMIT): break if loop%100==0 and best_xor >= perfect - (2**K-1): break # 近傍:A から a_val, Sb から b_val をランダムに選んで交換を試す a_val = Sa.random_element() b_val = Sb.random_element() # 交換後の Sa の XOR を定数時間で計算 new_xor = Sa.current_xor ^ a_val ^ b_val # 改善があれば実際にスワップ if new_xor > Sa.current_xor: Sa.remove(a_val) Sb.remove(b_val) Sa.add(b_val) Sb.add(a_val) best_xor = new_xor print(f"best: {loop=} {best_xor=}", file=sys.stderr) return Sa, Sb, best_xor ########################################### def main(DEBUG): tk = TimeKeeper() LIMIT = 1.2 N, M = map(int, input().split()) A = list(map(int, input().split())) # 5 <= N <= 10**5 # 1 <= M <= N # 1 <= Ai <= 10**6 # Ai != Aj # print(M, file=sys.stderr) # exit() A.sort() La = [] Lb = [] for k in range(10): K = k found = False for i in range(len(A)-1): a1 = A[i+1] a0 = A[i] if a1 - a0 == 2**k: # print(k, a0, a1, file=sys.stderr) La.append(a0) Lb.append(a1) A.remove(a0) A.remove(a1) found = True break if found == False: break xor_init = 0 for i in range(K): if La[i] != -1: xor_init ^= La[i] Sa, Sb, best = hill_climbing_xor2(A, M, K, xor_init, tk, LIMIT) ans = Sa.data[:] for k in range(K): if (best>>k) & 1 == 1: ans.append(La[k]) else: ans.append(Lb[k]) best += (1<<k) # print(*Sa.data) print(*ans) print("SC", best, file=sys.stderr) return if __name__ == '__main__': parser = argparse.ArgumentParser(description='Debug mode') parser.add_argument('--debug', action='store_true', help='Enable debug mode') args = parser.parse_args() main(args.debug)