Die Siedler
题目链接:ARC112F
题目大意
有 n 种牌,2i 个第 i 种牌可以变成一个第 i+1 种牌。
特别的 2n 个第 n 种牌会变成一个第 1 种牌。
然后有 m 种牌包,里面每种牌都有一定的数量,然后牌包数量无限,随便你用。
然后告诉你你一开始有的牌,你可以随意用牌随便变牌,问你手上多少会有多少牌。
思路
考虑到我们肯定是能换就换,因为一定会让牌数变少。
那我们考虑全部不换,甚至退回去,我们就看全部退回成第一种牌,有多少张。
对原来有的牌转化得到数量
R
R
R,每个卡包也转化得到
b
i
b_i
bi。
那首先会发现一个特别的就是从
n
n
n 转化到
1
1
1,发现少了
2
n
n
!
−
1
2^nn!-1
2nn!−1。
那最后牌数我们可以表示成这个:
v
=
R
+
∑
i
=
1
m
b
i
x
i
−
y
(
2
n
n
!
−
1
)
v=R+\sum\limits_{i=1}^mb_ix_i-y(2^nn!-1)
v=R+i=1∑mbixi−y(2nn!−1)
其中
x
i
,
y
x_i,y
xi,y 是让你选的。
v
−
R
=
∑
i
=
1
m
b
i
x
i
−
y
(
2
n
n
!
−
1
)
v-R=\sum\limits_{i=1}^mb_ix_i-y(2^nn!-1)
v−R=i=1∑mbixi−y(2nn!−1)
然后通过斐蜀定理,我们有
v
−
R
v-R
v−R 得是
gcd
(
b
1
,
b
2
,
.
.
.
,
b
m
,
2
n
n
!
−
1
)
\gcd(b_1,b_2,...,b_m,2^nn!-1)
gcd(b1,b2,...,bm,2nn!−1) 的倍数。
那假设上面
gcd
\gcd
gcd 的结果是
d
d
d。
考虑根号分治:
d
>
2
n
n
!
d>\sqrt{2^nn!}
d>2nn!
那这个时候我们可以试着暴力枚举倍数,然后直接转换会序列,算出答案。
d
⩽
2
n
n
!
d\leqslant\sqrt{2^nn!}
d⩽2nn!
这个时候我们可以试着用
f
i
f_i
fi 表示价值为
i
i
i 的最小换成的牌数。
然后转移是
f
(
i
+
2
k
−
1
(
k
−
1
)
!
)
m
o
d
d
=
min
(
f
(
i
+
2
k
−
1
(
k
−
1
)
!
)
m
o
d
d
,
f
i
+
1
)
f_{(i+2^{k-1}(k-1)!)\bmod d}=\min(f_{(i+2^{k-1}(k-1)!)\bmod d},f_i+1)
f(i+2k−1(k−1)!)modd=min(f(i+2k−1(k−1)!)modd,fi+1)
然后初始化是
f
2
k
−
1
(
k
−
1
)
!
m
o
d
d
=
1
f_{2^{k-1}(k-1)!\bmod d}=1
f2k−1(k−1)!modd=1,这个因为每次都是
+
1
+1
+1 我们可以 bfs 出结果,然后要的就是
f
R
m
o
d
d
f_{R\bmod d}
fRmodd
时间复杂度都是:
O
(
2
n
n
!
n
)
O(\sqrt{2^nn!}n)
O(2nn!n)
发现好像复杂度是 1 e 9 1e9 1e9 级别的。
但是呢,你发现
2
n
n
!
−
1
2^nn!-1
2nn!−1 这个数,它的质因子,比较的有特点。
(因为这个数是固定有的,而且只有
16
16
16 种情况)
发现要么很大(这个大是贴近
2
n
n
!
2^nn!
2nn! 不是
2
16
16
!
2^{16}16!
21616!),要么很小。
会发现最大的
d
d
d 大概在
1
e
6
1e6
1e6 的级别上,所以是可以过的。
代码
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N = 17;
const int M = 51;
const int B = 5e6 + 1000;
int n, m, a[N], ans, f[B];
ll R, b[M], n2n, GCD, jc[N];
bool in[B];
queue <int> q;
ll change() {
ll re = 0, sum = 1;
for (int i = 1; i <= n; i++) {
re += (1ll << i - 1) * jc[i - 1] * a[i];
}
return re;
}
int back(ll x) {
int re = 0;
for (int i = 1; i <= n; i++) {
re += x % (2 * i); x /= 2 * i;
}
return re;
}
ll gcd(ll x, ll y) {
if (!y) return x;
return gcd(y, x % y);
}
void bfs() {
while (!q.empty()) {
int now = q.front(); q.pop();
for (int i = 1; i <= n; i++) {
int to = (now + (1ll << i - 1) * jc[i - 1]) % GCD;
if (f[to] > f[now] + 1) {
f[to] = f[now] + 1;
if (!in[to]) {
in[to] = 1; q.push(to);
}
}
}
}
}
int main() {
scanf("%d %d", &n, &m);
jc[0] = 1; for (int i = 1; i <= n; i++) jc[i] = jc[i - 1] * i;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]); R = change();
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) scanf("%d", &a[j]); b[i] = change();
}
if (!R) {printf("0"); return 0;}
n2n = (1ll << n) * jc[n];
GCD = n2n - 1; for (int i = 1; i <= m; i++) GCD = gcd(GCD, b[i]);
ans = 2e9;
if (GCD <= sqrt(n2n)) {
memset(f, 0x7f, sizeof(f));
for (int i = 1; i <= n; i++) {
int k = (1ll << i - 1) * jc[i - 1] % GCD;
f[k] = 1; in[k] = 1; q.push(k);
}
bfs();
ans = f[R % GCD];
}
else {
for (ll val = R % GCD; val < n2n; val += GCD) {
if (!val) continue;
ans = min(ans, back(val));
}
}
printf("%d\n", ans);
return 0;
}