ARC112F Die Siedler

ARC112F Die Siedler

题目来源:Atcoder, ARC112F Die Siedler

题目大意

题目链接

你有若干张编号为 $ 1 $ 到 $ n $ 的卡片,和 $ m $ 包可以无限开的卡包,你可以以任意顺序执行任意多次以下操作:

  • 将 $ 2i $ 张编号为 $ i $ 的卡片换成一张编号为 $ i mod n + 1$ 的卡片。
  • 选择一个卡包打开并获得其中的所有卡片。

你希望你拥有的卡片张数尽可能的少。

你需要求出经过任意多次操作后你拥有的卡片的最小张数。

数据范围:(1leq nleq 16, 1leq mleq 50)

本题题解

定义与一些基本性质

定义一个“局面” (A) 是一个长度为 (n) 的非负整数序列,(A_i) 表示编号为 (i) 的卡片有多少张。

定义两个局面的加法,就是把同类型的卡片数量相加。即:((A + B)_i = A_i + B_i)

对于整数 (x),我们定义它的生成局面 (G(x)) 为:编号为 (1) 的卡片数量为 (x)、其他卡片数量为 (0) 的局面。

定义一次逆向操作,是把 (1) 张编号为 (i + 1) 的卡片,换成 (2i) 张编号为 (i) 的卡片((1leq i < n))。

定义 (g(A)) 表示把局面 (A) 里所有东西逆向操作到位置 (1) 后,能得到多少张卡片。即:(g(A) = sum_{i = 1}^{n}A_i2^{i - 1}(i - 1)!)

定义 (T(A)) 表示把局面 (A) 里所有东西逆向操作到位置 (1) 后,得到的局面。即,(T(A) = G(g(A)))

定义 (f(A)) 表示对 (A) 里的数使用若干次正向操作后,能得到的最小卡片张数。(f(A)) 可以用贪心法求出,也就是只要任意位置能使用操作,我们就用一下,显然操作的顺序不影响结果。

不难证明如下两条性质:

  • (f(A) = f(T(A)))
  • (f(A + B) = f(T(A) + T(B)))。(可加性)

此外我们发现,任意一个局面 (A),都可以通过 (n) 次正向操作,使得 (A_1) 减小 (2^{n}n! - 1),其他位置数值不变。其实就是把 (2^{n}n!) 张编号为 (1) 的卡片,依次换为编号为 (2, 3, dots,n) 的卡片,再换回来,变成 (1) 张编号为 (1) 的卡片。称这样的 (n) 次正向操作,为一个基本变换


暴力做法

特判最开始所有卡片数量都为 (0) 的情况。以下认为初始时至少存在一种卡片数量 $ > 0$。

把每个卡包也看做一个局面,记卡包 (i) 为局面 (C_i)。记初始局面为 (A)

因为 (f(A) = f(T(A))),且 (f(A + B) = f(T(A) + T(B)))。所以只需要考虑 (T(C_i))(T(A)),对它们操作,能够得到的最优局面是什么。使用一次卡包 (i),就相当于令 (g(A)gets g(A) + g(C_i))。换句话说,我们把每个局面(和卡包)都映射为了一个数(也就是 (g(A)))。把局面之间的变换,转化为了数的变换

(c_i = g(C_i))。设 (a = g(A))。那么最终能得到的,编号为 (1) 的卡片数量,可以写成如下形式:

[v = a + x_1 c_1 + x_2 c_2 + dots + x_n c_n - y(2^{n}n! - 1) ]

其中 (x_1dots x_n) 以及 (y) 都是非负整数(x_i) 表示开了多少次卡包 (i)(y) 表示使用了多少次基本变换。

我们的目标,就是在所有可能的正整数 (v) 中,找出 (f(G(v))) 最小的那个。

根据裴蜀定理,不定方程 (x_1 c_1 + x_2 c_2 + dots + x_n c_n - y(2^{n}n! - 1) = k) 有解,当且仅当 (k)(gcd(c_1, c_2, dots, c_n, (2^nn! - 1))) 的倍数。并且发现,只要满足该条件,就一定存在一组解使得 (x_1dots x_n)(y) 都非负。因为如果 (x_i < 0)(y < 0),可以令 (x_i exttt{+=}(2^nn! - 1), y exttt{+=} c_i),这样和是不变的,操作若干次后,总能让所有数非负。

(d = gcd(c_1, c_2, dots, c_n, (2^nn! - 1)))。那么问题转化为:求一个正整数 (v),满足 (vequiv apmod{d}),且 (f(G(v))) 最小。

(a' = amod d),那么我们可以从 (v = a', a' + d, a' + 2ddots) 一直枚举下去,看哪个 (v) 得到的局面最优。当然,枚举到 (2^nn! - 1) 后就不用再枚举了,因为循环了。具体来说,我们要求的是:

[min_{substack{tgeq 0\a' + tdleq 2^nn!-1\a' + td eq 0}}{f(G(a' + td))} ]

暴力枚举 (t),暴力计算 (f),则时间复杂度 (mathcal{O}(2^nn! cdot n))


优化

我们上述的枚举量,实际是 (mathcal{O}(frac{2^nn! - 1}{d})) 级别的,所以考虑对 (d) 进行根号分治

(dgeq sqrt{2^nn! - 1}),直接按上述方法暴力。时间复杂度 (mathcal{O}(frac{2^nn! - 1}{d}cdot n)= mathcal{O}(sqrt{2^nn! - 1}cdot n))

(d < sqrt{2^nn! - 1}),跑同余最短路。问题相当于求,(g(S)mod d = a') 的所有局面 (S) 里,(f(S)) 最小是多少。我们可以对所有 (0leq i < d), (1leq jleq n),从 (i)((i + 2^{j - 1}(j - 1)!)mod d) 连边,边权为 (1)。起点是所有 (2^{j - 1}(j - 1)!),初始权值为 (1)(起点不能设为 (0),因为 (v) 必须是正数)。求到点 (a') 的最短路即可。时间复杂度是 (mathcal{O}(dcdot n) = mathcal{O}(sqrt{2^nn! - 1}cdot n)) 的。

注意到当 (nleq16) 时,(2^nn! - 1) 的因数中 (leq sqrt{2^nn!- 1}) 的最大因数为 (1214827)。因此上述做法可以通过本题。

参考代码

// problem: ARC112F
#include <bits/stdc++.h>
using namespace std;

#define mk make_pair
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }

const int MAXN = 16, MAXM = 50;
const int INF = 1e9;
const ll LL_INF = 1e18;

int n, m;
ll coef[MAXN + 5];

struct State {
	int a[MAXN + 5];
	ll val;
	ll read() {
		val = 0;
		for (int i = 1; i <= n; ++i) {
			cin >> a[i];
			val += coef[i - 1] * a[i];
		}
		return val;
	}
	State() {}
};

State A, C[MAXM + 5];

ll gcd(ll x, ll y) { return (!y) ? x : gcd(y, x % y); }

int main() {
	cin >> n >> m;
	coef[0] = 1;
	for (int i = 1; i <= n; ++i) {
		coef[i] = coef[i - 1] * 2 * i;
	}
	
	ll a = A.read();
	
	if (!a) {
		cout << 0 << endl;
		return 0;
	}
	
	ll mod = coef[n] - 1;
	ll d = mod;
	
	for (int i = 1; i <= m; ++i) {
		d = gcd(C[i].read(), d);
	}
	
	if (d < mod / d) {
		
		vector<int> dis(d);
		queue<int> q;
		
		for (int i = 0; i < d; ++i) {
			dis[i] = INF;
		}
		for (int i = 0; i < n; ++i) {
			dis[coef[i] % d] = 1;
			q.push(coef[i] % d);
		}
		
		while (!q.empty()) {
			int u = q.front();
			q.pop();
			
			for (int i = 0; i < n; ++i) {
				int v = (u + coef[i]) % d;
				if (dis[v] > dis[u] + 1) {
					dis[v] = dis[u] + 1;
					q.push(v);
				}
			}
		}
		
		cout << dis[a % d] << endl;
	} else {
		ll ans = LL_INF;
		
		for (ll v = a % d; v <= mod; v += d) {
			if (v == 0) continue;
			
			ll f = 0;
			ll vv = v;
			for (int i = 1; i <= n; ++i) {
				// f += vv % (2 * i);
				// vv /= 2 * i;
				f += vv % coef[i] / coef[i - 1];
			}
			ckmin(ans, f);
		}
		
		cout << ans << endl;
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/14423706.html