Official
E - トーナメント分割の均衡グループ / Balanced Groups in Tournament Partition Editorial
by
E - トーナメント分割の均衡グループ / Balanced Groups in Tournament Partition Editorial
by
kyopro_friends
文字 0, 1 と、それを数値として解釈したときの値を同一視します。
\(N\) 人からなるグループが均衡グループであることは、その区間の和が \(N/2\) であることと同値です。よって、均衡グループであるかどうかを判定するためには、区間和が分かればよさそうです。
\(S\) に対して \(l\) を選んで操作してできる文字列 \(T_l\) とします。
\(T_l\) と \(T_{l+1}\) は、「\(l\) 文字目」「\(l+K\) 文字目」「区間内の 0, 1 の境界」の高々 \(4\) 文字しか違いません。

よって \(T_l\) と \(T_{i+1}\) で均衡グループかどうかを再考慮する必要があるグループの個数は高々 \(4N\) 個です。それぞれについて区間和の変化を調べることで、均衡グループの個数の変化を求めることができます。
例:4文字目を 0 から 1 に変更した際の、各グループに対応する区間の区間和の更新

以上を元に差分更新を行うことで、\(l\) の昇順に全体として \(O(2^N N)\) 時間でこの問題を解くことができます。
実装例 (C++)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, k;
cin >> n >> k;
string ss;
cin >> ss;
vector<int> s(1<<n);
for(int i=0; i<1<<n; i++){
if(ss[i] == '0'){
s[i] = 0;
}else{
s[i] = 1;
}
}
// 操作をしない場合
int ans = 0;
for(int w=0; w<=n; w++){
for(int i=0; i<1<<n; i+=1<<w){
if(accumulate(s.begin()+i, s.begin()+i+(1<<w), 0) * 2 == (1<<w)){
ans++;
}
}
}
// l=0で操作を行い、区間内の0の個数と、各グループの総和・均衡グループの個数を記録
vector<int> t=s;
sort(t.begin(), t.begin()+k);
int count0 = count(t.begin(), t.begin()+k, 0);
vector<vector<int>>count(n+1);
int crr = 0;
for(int w=0; w<=n; w++){
for(int i=0; i<1<<n; i+=1<<w){
int c = accumulate(t.begin()+i, t.begin()+i+(1<<w), 0);
count[w].push_back(c);
if(c * 2 == (1<<w)){
crr++;
}
}
}
auto upd=[&](int i, int v){
// T[i] を v に変更し、各グループの総和・均衡グループの個数を更新
if(t[i] == v){
return;
}
for(int w=0; w<=n; w++){
int ii = i>>w;
if(count[w][ii] * 2 == (1<<w)){
crr--;
}
count[w][ii] -= t[i];
count[w][ii] += v;
if(count[w][ii] * 2 == (1<<w)){
crr++;
}
}
t[i] = v;
};
for(int i=0; i<(1<<n)-k; i++){
// [i,i+k) から [i+1,i+k+1) に変更
// i を削除
if(s[i] == 0){
count0--;
}
if(t[i] != s[i] && count0 != 0){
upd(i, 1);
upd(i+count0, 0);
}
//i+k を追加
if(s[i+k] == 0){
count0++;
}
if(s[i+k] == 0 && count0 != k){
upd(i+count0, 0);
upd(i+k, 1);
}
ans = max(ans, crr);
}
cout << ans << endl;
}
実装例 (Python)
N, K = map(int, input().split())
S = list(map(int, input()))
# 操作をしない場合
ans = 0
for w in range(N+1):
for i in range(0, 2**N, 2**w):
if sum(S[i:i+2**w]) * 2 == 2**w:
ans += 1
# l=0で操作を行い、区間内の0の個数と、各グループの総和・均衡グループの個数を記録
T = list(S)
T[:K] = sorted(T[:K])
count0 = K - sum(T[:K])
count = [[] for _ in range(N+1)]
crr = 0
for w in range(N+1):
for i in range(0, 2**N, 2**w):
c = sum(T[i:i+2**w])
count[w].append(c)
if c * 2 == 2**w:
crr += 1
def upd(i, v):
# T[i] を v に変更し、各グループの総和・均衡グループの個数を更新
global crr
if T[i] == v:
return
for w in range(N+1):
ii = i // (2**w)
if count[w][ii] * 2 == 2**w:
crr -= 1
count[w][ii] -= T[i]
count[w][ii] += v
if count[w][ii] * 2 == 2**w:
crr += 1
T[i] = v
for i in range(2**N-K):
# [i,i+K) から [i+1,i+K+1) に変更
# i を削除
if S[i] == 0:
count0 -= 1
if T[i] != S[i] and count0 != 0:
upd(i, 1)
upd(i+count0, 0)
# i+K を追加
if S[i+K] == 0:
count0 += 1
if S[i+K] == 0 and count0 != K:
upd(i+count0, 0)
upd(i+K, 1)
ans = max(ans, crr)
print(ans)
posted:
last update:
