【ARC112F】Die Siedler(根号分治)(bfs)
Die Siedler
题目链接:ARC112F
题目大意
有 n 种牌,2i 个第 i 种牌可以变成一个第 i+1 种牌。
特别的 2n 个第 n 种牌会变成一个第 1 种牌。
然后有 m 种牌包,里面每种牌都有一定的数量,然后牌包数量无限,随便你用。
然后告诉你你一开始有的牌,你可以随意用牌随便变牌,问你手上多少会有多少牌。
思路
考虑到我们肯定是能换就换,因为一定会让牌数变少。
那我们考虑全部不换,甚至退回去,我们就看全部退回成第一种牌,有多少张。
对原来有的牌转化得到数量 \(R\),每个卡包也转化得到 \(b_i\)。
那首先会发现一个特别的就是从 \(n\) 转化到 \(1\),发现少了 \(2^nn!-1\)。
那最后牌数我们可以表示成这个:
\(v=R+\sum\limits_{i=1}^mb_ix_i-y(2^nn!-1)\)
其中 \(x_i,y\) 是让你选的。
\(v-R=\sum\limits_{i=1}^mb_ix_i-y(2^nn!-1)\)
然后通过斐蜀定理,我们有 \(v-R\) 得是 \(\gcd(b_1,b_2,...,b_m,2^nn!-1)\) 的倍数。
那假设上面 \(\gcd\) 的结果是 \(d\)。
考虑根号分治:
\(d>\sqrt{2^nn!}\)
那这个时候我们可以试着暴力枚举倍数,然后直接转换会序列,算出答案。
\(d\leqslant\sqrt{2^nn!}\)
这个时候我们可以试着用 \(f_i\) 表示价值为 \(i\) 的最小换成的牌数。
然后转移是 \(f_{(i+2^{k-1}(k-1)!)\bmod d}=\min(f_{(i+2^{k-1}(k-1)!)\bmod d},f_i+1)\)
然后初始化是 \(f_{2^{k-1}(k-1)!\bmod d}=1\),这个因为每次都是 \(+1\) 我们可以 bfs 出结果,然后要的就是 \(f_{R\bmod d}\)
时间复杂度都是:
\(O(\sqrt{2^nn!}n)\)
发现好像复杂度是 \(1e9\) 级别的。
但是呢,你发现 \(2^nn!-1\) 这个数,它的质因子,比较的有特点。
(因为这个数是固定有的,而且只有 \(16\) 种情况)
发现要么很大(这个大是贴近 \(2^nn!\) 不是 \(2^{16}16!\)),要么很小。
会发现最大的 \(d\) 大概在 \(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;
}

浙公网安备 33010602011771号