← 全部文章

CF516E Drazil and His Happy Friends 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

CF516E Drazil and His Happy Friends

题意

  • 有 nn 个男生 mm 个女生,编号分别为 0∼n−10 \sim n - 1 和 0∼m−10 \sim m - 1。
  • 有 bb 个男生和 gg 个女生是快乐的,其他人是不快乐的。
  • 在第 ii 天,编号为 i mod ni \bmod n 的男生和编号为 i mod mi \bmod m 的女生会一起玩。
  • 如果他们俩中有一个人是快乐的,则另一个人也会变快乐。
  • 求至少要多少天所有人都会变快乐,或者判断不可能所有人都变快乐。
  • n,m≤109n,m \le 10^9,b,g≤105b,g \le 10^5。

题解

首先考虑 gcd⁡(n,m)≠1\gcd(n,m) \ne 1,我们可以按照  mod gcd⁡(n,m)\bmod \gcd(n,m) 分组,不同组之间显然互不影响。

那么根据裴蜀定理,有解的充要条件为每组内至少有一个人是快乐的。

若有解,将每一组的答案求出来后取 max⁡\max 即可。

考虑某一组,此时不妨设 n>mn > m,同时有 gcd⁡(n,m)=1\gcd(n,m) = 1。

首先有一个结论:**如果 nn 个男生都快乐了,那么除非当前天数小于 mm,否则所有女生一定也都是快乐的。**这是因为往前的 mm 天里,由于所有男生都快乐了,因此要么是原本就都快乐,要么是快乐的影响另一个。

那么我们只需要求出最后一个变快乐的男生即可。为了方便,如果女生 xx 初始快乐,那么将男生 xx 也标记为初始快乐。

对于一个男生 xx,若它在第 tt 天变快乐,那么他就会通过女生 t mod mt \bmod m 在第 t+mt+m 天使男生 (t+m) mod n(t+m)\bmod n 变快乐。

考虑同余最短路,则有连边 i⟶m(i+m) mod ni \stackrel{m}{\longrightarrow} (i+m)\bmod n。另外,建立一个虚拟源点 SS,对于初始快乐的男生 ii,有连边 S⟶iiS \stackrel{i}{\longrightarrow} i。

则最后所有点的最短路的最大值就是这一组的答案。

但是这样点数是 10910^9 级别的,无法承受。

注意到这样的建图方式实际上会形成一个环,因此考虑删掉一些并不重要的点:如果 ii 和 (i+m) mod n(i+m) \bmod n 初始都是不快乐的,那么 ii 一定会在 (i+m) mod n(i+m) \bmod n 之前变快乐,因此 ii 就没用了。

那么我们可以只保留 ii 或 (i+m) mod n(i+m) \bmod n 初始快乐的节点,这样点数只有 10510^5 级别了。

如何进行连边呢?

设 ii 初始快乐的节点为 AA 类点,编号为 AiA_i;(i+m) mod n(i+m) \bmod n 初始快乐的节点为 BB 类点,编号为 BiB_i。

我们要连三类边:

  1. S⟶iAiS \stackrel{i}{\longrightarrow} A_i
  2. Bi⟶mAiB_i \stackrel{m}{\longrightarrow} A_i
  3. Ai⟶xmBjA_i \stackrel{xm}{\longrightarrow} B_j

第 1,21,2 类边都很容易,问题在第 33 类边上。要求出 BjB_j,求出 AjA_j 即可,那么此时实际上问题转化为在模 nn 意义下以 mm 为跨度的有向环上,从每一个 AiA_i 往后遇到的第一个 AjA_j 是哪个。

我们可以考虑 ii 最小的 AiA_i,求出从 AiA_i 开始分别要经过多少个 mm 到达其他 AA 类点 AjA_j,即求 i+xm≡j(modn)i + xm \equiv j \pmod n 的最小非负整数解 xx,使用 exgcd 即可。注意实现时其实只用求一次 exgcd。

求完后,对所有 xx 排序,即可求出环上 AA 类点依次被经过的顺序,也就求出了每一个 AiA_i 往后遇到的第一个 AjA_j 是哪个。

这样第 33 类边也能够连上了。

注意最后还要特判天数小于 mm 的情况。

总时间复杂度 O(nlog⁡n)\mathcal O(n \log n)。

代码

#define Fail return print(-1), 0

const int N = 1e5 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, m, d, B, G, b[N], g[N];
vi a[N<<1], f[N<<1];
ll ans;

int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

int exgcd(int a, int b, int &x, int &y, int d = 0) { return b ? (d = exgcd(b, a % b, y, x), y -= a / b * x, d) : (x = 1, y = 0, a); }

inline ll solve(vi a, vi b) {
	vi s = a;
	for (ui i = 0; i < b.size(); i++) s.pb(b[i]);
	sort(s.begin(), s.end());
	int k = unique(s.begin(), s.end()) - s.begin(), x, y;
	if (n == k) {
		int c[m];
		memset(c, 0, sizeof(c));
		for (ui i = 0; i < a.size(); i++) if (a[i] < m) ++c[a[i]];
		for (ui i = 0; i < b.size(); i++) if (b[i] < m) ++c[b[i]];
		for (int i = m - 1; ~i; i--) if (c[i] == 1) return i;
		return -1;
	}
	vector< pair< int, ll > > e[2*k+1];
	for (int i = 0; i < k; i++) e[2*k].pb(mp(i, s[i])), e[i+k].pb(mp(i, m));
	exgcd(m, n, x, y), x = (x % n + n) % n;
	pi p[k];
	for (int i = 0; i < k; i++) p[i] = mp(1ll * x * (s[i] - s[0]) % n, i);
	sort(p, p + k);
	for (int i = 0, j = 1 % k; i < k; i++, j = (++j == k) ? 0 : j) e[p[i].se].pb(mp(p[j].se + k, 1ll * m * (p[j].fi - p[i].fi - 1 < 0 ? p[j].fi - p[i].fi - 1 + n : p[j].fi - p[i].fi - 1)));
	map< int, int > h;
	for (int i = 0; i < k; i++) h[s[i]] = i;
	for (int i = 0; i < k; i++)
		if (h.find((s[i] + m) % n) != h.end()) {
			int j = h[(s[i]+m)%n];
			e[i].pb(mp(j + k, 0)), e[j+k].pb(mp(i, 0));
		}
	pq< pair< ll, int > > q;
	int v[2*k+1];
	ll d[2*k+1], now = 0;
	memset(v, 0, sizeof(v));
	memset(d, 0x3f, sizeof(d));
	q.push(mp(0, 2 * k)), d[2*k] = 0;
	while (q.size()) {
		int x = q.top().se;
		q.pop();
		if (v[x]) continue;
		v[x] = 1, now = max(now, d[x]);
		for (ui i = 0; i < e[x].size(); i++) {
			int y = e[x][i].fi;
			ll z = e[x][i].se;
			if (d[y] > d[x] + z) d[y] = d[x] + z, q.push(mp(-d[y], y));
		}
	}
	return now;
}

int main() {
	rd(n), rd(m), rd(B);
	for (int i = 1; i <= B; i++) rd(b[i]);
	rd(G);
	for (int i = 1; i <= G; i++) rd(g[i]);
	if (n < m) swap(n, m), swap(B, G), swap(b, g);
	if ((d = gcd(n, m)) > B + G) Fail;
	n /= d, m /= d;
	for (int i = 1; i <= B; i++) a[b[i]%d].pb(b[i] / d);
	for (int i = 1; i <= G; i++) f[g[i]%d].pb(g[i] / d);
	for (int i = 0; i < d; i++)
		if (!a[i].size() && !f[i].size()) Fail;
	for (int i = 0; i < d; i++) ans = max(ans, solve(a[i], f[i]) * d + i);
	print(ans);
	return 0;
}

标签:同余最短路、数论、最短路

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…