BZOJ 1963 最小和

题目:BZOJ 1963 最小和
(笔者注:出处似乎是 TopCoder SRM 456 Div.1 1050pts FunctionalEquation)

题意:

给定 CCNN,并用伪随机的方法给出 (xi,yi)(x_i, y_i) (i=0,1,,N1)(i = 0, 1, \ldots, N - 1),找到一个整数到整数的映射 f:ZZf : \mathbb{Z} \to \mathbb{Z},使得对于任意整数 xx,有 f(2f(x)x+1)=f(x)+Cf(2 f(x) - x + 1) = f(x) + C,在此基础上最小化 i=0N1f(xi)yi\sum_{i = 0}^{N - 1}{|f(x_i) - y_i|}

1C161 \leq C \leq 16, 1N1041 \leq N \leq 10^4, 0xi,yi<1090 \leq x_i, y_i < 10^9

题解:

观察 ff 的限制条件,有
f(x)+2C=(f(x)+C)+C=f(2f(x)x+1)+C=f(2f(2f(x)x+1)(2f(x)x+1)+1)=f(2(f(x)+C)2f(x)+x)=f(x+2C)\begin{aligned} & f(x) + 2 C \\ = & (f(x) + C) + C \\ = & f(2 f(x) - x + 1) + C \\ = & f(2 f(2 f(x) - x + 1) - (2 f(x) - x + 1) + 1) \\ = & f(2(f(x) + C) - 2 f(x) + x) \\ = & f(x + 2 C) \end{aligned}
于是只要确定了 f(0),f(1),,f(2C1)f(0), f(1), \ldots, f(2 C - 1) 的取值,必然能确定整个映射。

注意到在 f(x)f(x) 确定的同时,除了 f(x+2kC)f(x + 2 k C) (kZ)(k \in \mathbb{Z}) 的取值会确定,它也会确定 f(2f(x)x+1+2kC)f(2 f(x) - x + 1 + 2 k C) (kZ)(k \in \mathbb{Z}) 的取值,而 xx(2f(x)x+1)(2 f(x) - x + 1) 奇偶性不同,所以它们在模 2C2 C 意义下一定不相同,进一步推理可得,模意义下的每个奇数剩余类恰好会和一个偶数剩余类配对。

不失一般性地,令 x2u,2f(x)x+12v+1(mod2C)x \equiv 2 u, 2 f(x) - x + 1 \equiv 2 v + 1 \pmod{2C} (0u,v<C)(0 \leq u, v < C),那么有
f(2u)f(2v+1)u+v(modC)f(2 u) \equiv f(2 v + 1) \equiv u + v \pmod{C}

进一步地,若
f(2u)=u+v+aC (aZ)f(2 u) = u + v + a C~(a \in \mathbb{Z})
那么有
f(2v+1)=f(2v+1+2aC)2aC=f(2(u+v+aC)2u+1)2aC=f(2f(2u)2u+1)2aC=(u+v+aC)+C2aC=u+v+(1a)C\begin{aligned} & f(2 v + 1) \\ = & f(2 v + 1 + 2 a C) - 2 a C \\ = & f(2(u + v + a C) - 2 u + 1) - 2 a C \\ = & f(2 f(2 u) - 2 u + 1) - 2 a C \\ = & (u + v + a C) + C - 2 a C \\ = & u + v + (1 - a) C \end{aligned}

于是,对于一组剩余类配对 {(2u1,2v1+1),(2u1,2v1+1),,(2uk,2vk+1),,(2uC,2vC+1)}\lbrace (2 u_1, 2 v_1 + 1), (2 u_1, 2 v_1 + 1), \ldots, (2 u_k, 2 v_k + 1), \ldots, (2 u_C, 2 v_C + 1) \rbrace,我们只需要分别最小化 xi2uk(mod2C)f(xi)yi+xi2vk+1(mod2C)f(xi)yi\sum_{x_i \equiv 2 u_k \pmod{2 C}}{|f(x_i) - y_i|} + \sum_{x_i \equiv 2 v_k + 1 \pmod{2 C}}{|f(x_i) - y_i|} 即可。

对于一组 (u,v)(u, v),我们可以将有关的 f(xi)yi|f(x_i) - y_i| 均写成 aCpi|a C - p_i| 的形式,而 iaCpi\sum_{i}{|a C - p_i|} 是关于整数 aa 的分段一次函数,只需要枚举每个段里 aa 能取到的最左或最右的位置即可确定最小值。

剩下的部分是如何求出最优的剩余类配对。在预处理每对 (u,v)(u, v) 配对的最小代价后,计算二分图最小权完美匹配即可。由于 CC 比较小,我们可以偷点懒,用 O(C2C)\mathcal{O}(C 2^C) 的方法计算匹配。

偷懒后的总时间复杂度为 O(nlogn+nC+C2+C2C)\mathcal{O}(n \log n + n C + C^2 + C 2^C),其中 O(C2C)\mathcal{O}(C 2^C) 的部分可以优化到 O(C3)\mathcal{O}(C^3)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = (int)1e4 + 1, maxd = 17, maxs = 1 << 16 | 1, INF = 0x3f3f3f3f;
int n, m;
vector<int> lft[maxd], rht[maxd];
LL w[maxd][maxd], f[maxs];
struct Gen {
	int val, prd, adt, mod;
	void read() {
		scanf("%d%d%d%d", &val, &prd, &adt, &mod);
	}
	int get() {
		int ret = val;
		val = ((LL)val * prd + adt) % mod;
		return ret;
	}
} genX, genY;
inline void upd(LL &x, LL y) {
	x > y && (x = y);
}
int main() {
	scanf("%d%d", &m, &n);
	genX.read();
	genY.read();
	for(int i = 0; i < n; ++i) {
		int x = genX.get(), y = genY.get();
		if(x & 1) {
			int v = (x + m + m - 1) / 2 % m;
			rht[v].push_back(y - x - m + 1);
		} else {
			int u = x / 2 % m;
			lft[u].push_back(x - y);
		}
	}
	for(int i = 0; i < m; ++i) {
		sort(lft[i].begin(), lft[i].end());
		sort(rht[i].begin(), rht[i].end());
	}
	for(int u = 0; u < m; ++u)
		for(int v = 0; v < m; ++v) {
			int len = 0;
			static int seq[maxn];
			for(vector<int>::iterator it = lft[u].begin(); it != lft[u].end(); ++it)
				seq[len++] = *it;
			for(vector<int>::iterator it = rht[v].begin(); it != rht[v].end(); ++it)
				seq[len++] = *it;
			inplace_merge(seq, seq + lft[u].size(), seq + len);
			LL sL = 0, sR = 0;
			for(int i = 0; i < len; ++i) {
				seq[i] += v - u;
				sR += seq[i];
			}
			int pos = seq[0] / m * m;
			if(pos > seq[0])
				pos -= m;
			w[u][v] = sR - (LL)len * pos;
			seq[len] = INF;
			for(int i = 1; i <= len; ++i) {
				sL += seq[i - 1];
				sR -= seq[i - 1];
				if(i < len - i) {
					pos = seq[i] / m * m;
					if(pos > seq[i])
						pos -= m;
					if(pos >= seq[i - 1])
						upd(w[u][v], sR - sL + (LL)(i + i - len) * pos);
				} else {
					pos = seq[i - 1] / m * m;
					if(pos < seq[i - 1])
						pos += m;
					if(pos <= seq[i])
						upd(w[u][v], sR - sL + (LL)(i + i - len) * pos);
				}
			}
		}
	for(int i = 0; i < (1 << m); ++i) {
		int cnt = 0;
		for(int j = 0; j < m; ++j)
			cnt += (i >> j) & 1;
		if(!cnt) {
			f[i] = 0;
			continue;
		}
		bool vis = 0;
		for(int j = 0; j < m; ++j) {
			if(!((i >> j) & 1))
				continue;
			LL tmp = f[i ^ (1 << j)] + w[cnt - 1][j];
			if(vis) {
				upd(f[i], tmp);
			} else {
				f[i] = tmp;
				vis = 1;
			}
		}
	}
	printf("%lld\n", f[(1 << m) - 1]);
	return 0;
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值