バイトの競プロメモ

主に競技プログラミング

C - Remainder Game

https://atcoder.jp/contests/agc022/tasks/agc022_c

 

思考

・2のべき乗なので、なるべく小さいkを使いたい

・同じkを複数回使う事はない

 

kを50から順に見ていき何かしたい。が、よく分からない

 

とりあえずaiから作れる数を考えてみる

ai        bi

1   :     0

2   :     0 

3   :     0 1

4   :     0 1 

5   :     0 1 2

6   :     0 1 2

 

 ai  >=  bi * 2 + 1 の時にaiはbiに出来る事が分かった

 

よって、ok(a,b) :=上の式を満たすか判定とすると

kを50から順に見ていき、あるiでok(ai % m , b)を満たす最小のmがkに等しい時、kを選ぶ必要があり、満たさなければkは必要ない     

O(n ^ 3)

 

しかしこれでは不十分である

選んだkは自由に使えるので、ナップザックのように「現状aiから作れる数」を持ち

aiのみでは無く、それらに対しても判定をしなくてはいけない

 

 

 

従って、あるiであって、aiから作れる数に対してok( ..)を満たす最小のm達の内、

それらの中で最小のmがkに等しい時、kを選ばなくてはいけない

ということになった

計算量はO(n ^ 4)

 

Submission #4570541 - AtCoder Grand Contest 022

void solve() {
cin >> n;
na(a, n);
na(b, n);
rep(i, n) {
if (a[i] < b[i])fin(-1);
if (b[i] == 0)con;
if (a[i] == b[i])con;
if (a[i] < b[i] * 2 + 1)fin(-1);
}

bool can[55][55] = {};
rep(i, n) {
can[i][a[i]] = 1;
}

rer(v, 50) {
bool need = 0;
rep(i, n) {
int mineed = inf;
if (can[i][b[i]])con;
rep(h, 51) {
if (can[i][h] == 0)con;
int m = 0;
rep(_, 51) {
m++;
if (ok(h % m, b[i]))brk;
}
chmin(mineed, m);
}
if (mineed == v) {
need = 1;
brk;
}
}
if (need) {
rep(i, n) {
rep(j, 51) {
if (can[i][j] == 0)con;
can[i][j % v] = 1;
}
}
cou += 1ll << v;
}
}
cout << cou << endl;
}