解説
2つのDPを更新して求めます。
- dp_byBattle[試合ID]: その試合終了時に、試合の勝者が持っている可能性のあるカードの数の集合
- dp_byWinner[試合ID][その試合の勝者]: 試合で(その試合の勝者)が勝ったとき、持っている可能性のあるカードの数の集合
計算量はO()ですが、持っている数の集合をbitsetで持ち、いわゆる64倍高速化を行うことで、時間制限に間に合います。
数の集合を2進数に変換する方法
数の集合を以下のように2進数に変換します。
- 集合を、に変換する。
- 例えば、{0, 2, 5}はに変換する。
試合後に持っている可能性のあるカードの数の集合の更新方法
集合と集合を持っており、前者が勝利した場合、前者は集合の重複を除いたものを持ちます。
貪欲に行ってもこの更新の計算量はO()ですが、bitsetを用いた高速化を行います。
- 簡単にするため、勝者のもつ集合の各要素をあらかじめ2倍に変換しておく。
- 集合Bを2進数に変換したものを、集合Cを2進数に変換したものをとする。
- 変換結果を表す変数を用意し、0で初期化する。
- について、の bit目が1であれば、を | (を回左シフトした値、B桁目以降は0桁目へローテートする)で更新する。
実装例(C++)
#include <iostream>
#include <bitset>
#include <vector>
using namespace std;
using ll = long long;
using BIT = bitset<109>;
ll N, M;
void bitShift(BIT& b){
b <<= 1;
if(b.test(M)){
b.reset(M);
b.set(0);
}
}
BIT times2(BIT x){
BIT result = 0;
for(int i = 0; i < M; ++i){
if(x.test(i)){
result.set((2 * i) % M);
}
}
return result;
}
BIT prod(BIT x, BIT y){
BIT result = 0;
for(int i = 0; i < M; ++i){
if(y.test(i)){
result |= x;
}
bitShift(x);
}
return result;
}
int main() {
cin >> N >> M;
ll cntPlayer = 1 << N;
vector<ll> A(cntPlayer);
for(int i = 0; i < cntPlayer; ++i){
cin >> A.at(i);
}
vector<BIT> dp_byBattle(2 * cntPlayer, 0);
vector<vector<BIT>> dp_byWinner(N + 1, vector<BIT>(cntPlayer, 0));
for(int i = 0; i < cntPlayer; ++i){
dp_byWinner.at(N).at(i).set(A.at(i));
dp_byBattle.at(cntPlayer + i).set(A.at(i));
}
for(int i = cntPlayer - 1; i >= 1; --i){
dp_byBattle.at(i) |= prod(times2(dp_byBattle.at(2 * i)), dp_byBattle.at(2 * i + 1));
dp_byBattle.at(i) |= prod(dp_byBattle.at(2 * i), times2(dp_byBattle.at(2 * i + 1)));
}
for(int i = N - 1; i >= 0; --i){//ステージ数(逆から)
int cntBattle = 1 << i;
for(int j = 0; j < cntBattle; ++j){//対戦
for(int k = 0; k < cntPlayer / cntBattle; ++k){//対戦での勝利者
int id = j * (cntPlayer / cntBattle) + k;
if(k < cntPlayer / cntBattle / 2){//対戦で左側
dp_byWinner.at(i).at(id) |= prod(times2(dp_byWinner.at(i + 1).at(id)), dp_byBattle.at(2 * ((1 << i) + j) + 1));
}else{//対戦で右側
dp_byWinner.at(i).at(id) |= prod(times2(dp_byWinner.at(i + 1).at(id)), dp_byBattle.at(2 * ((1 << i) + j)));
}
}
}
}
for(int i = 0; i < cntPlayer; ++i){
vector<int> ans;
for(int j = 0; j < M; ++j){
if(dp_byWinner.at(0).at(i).test(j)){
ans.push_back(j);
}
}
cout << (int)ans.size();
for(int e : ans){
cout << " " << e;
}
cout << endl;
}
return 0;
}
ユーザー解説
ユーザー解説を書くユーザー解説はまだありません。