CF516E Drazil and His Happy Friends
思路
我们令 \(d=\gcd(n, m)\),那么只有编号对 \(d\) 取模相同的学生才能互相影响
那么就将所有学生按取模后的结果分成了 \(d\) 个组,每个组的问题是独立的
如果有 \(d>b+g\),那么说明肯定有组中没有快乐的学生,直接特判掉,现在 \(d\) 就降为 \(2e5\) 的规模
对于第 \(x\) 个男 / 女生,\(x\%d=i\),那么他就应该被分到第 \(i\) 个组,重新编号为 \(x/d\)
对于每个组,我们都求出一个最大天数 \(p\),将 \(p\times d + i\) 与最终答案取最大值
现在我们考虑怎么求出每个组的最大值:注意,下文的 \(n, m\) 均除以了 \(d\)
(对于所有学生一开始都是快乐的组,我们直接特判掉,以免影响答案)
我们假设最后一个变快乐的女生为 \(i\),她是第 \(x\) 天变快乐的,那么有 \(x\% m=i\)
再假设是第 \(j\) 号男生让她开心的,那么又有 \(x\% n = j\)
假如这个男生一开始就是快乐的,那么第 \(j\) 天他会让 \(j\% m\) 号女生变快乐
我们令 \(z = (x - j)/n\),就是这两个女生比变快乐中间经过的轮数
\(j\) 号男生接下来会在第 \(j+n\) 天让第 \((j+n)\% m\) 号女生变快乐,其实我们是不是可以看做是 \(j\% m\) 号女生让 \((j+n)\% m\) 号女生变快乐,一直传递,最后到第 \(i\) 号女生
这样,我们就可以考虑这些“相邻”的女生连一条边权为 \(n\) 的边
然后,对于一开始就快乐的女生 \(x\),我们可以从虚源点向 \(x\) 连一条边权为 \(x\) 的边
对于一开始就快乐的男生 \(x\),我们可以从 虚源点向 \(x\% m\) 连一条边权为 \(x\) 的边
我们建好图后,就跑一遍最短路,然后再求出最短路中的最大值即可
但是,点的数量是 \(1e9\) 级别的,显然是无法完成的任务
我们可以发现这个图是一个有向环,加上一些入边组成的
对于那些没有入边的点,显然是从上一个有入边的点转移过来的最短路
那么我们其实可以只做那些有入边的点的最短路,算出两个点直接差多少轮,然后加上(轮次减一)\(\times\) \(n\) 就可以知道最远的距离
但我们怎么知道这些特殊点是什么顺序呢?
我们可以选择一个点固定下来,计算其他点与他相差多少轮,然后按照轮次从小到大排序
比如固定了点 \(p_i\),计算点 \(p_j\),应该有 \(p_i+xn\pmod m\),\(x\) 就是我们要求的轮次,这个其实可以用 exgcd
来求解
这样我们就做完了
这道题的细节是巨多的,需要仔细纠错,如注意数组大小,虚源点的编号选择等
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<unordered_map>
#include<set>
#include<bitset>
#define LL long long
#define oo 200001
inline int reads()
{
int sign = 1, re = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
return sign * re;
}
int n, m, b, g, d, X, Y;
int h1[100005], h2[100005];
std::vector<int> f[2][200005];
LL sum, ans; std::bitset<200005> vis;
void exgcd(int a, int b, int &x, int &y)
{
if(!b) {x = 1, y = 0; return;}
exgcd(b, a % b, y, x);
y -= a / b * x;
}
struct st
{
int num, x, w;
bool operator < (const st b) {return x < b.x;}
}p[200005]; int pcnt;
struct Node
{
int to; LL w; int nxt;
}r[400005]; int he[200005], rcnt;
inline void Edge_add(int u, int v, LL w)
{
r[++rcnt] = {v, w, he[u]};
he[u] = rcnt;
}
LL dis[200005];
#define pii std::pair<LL, int>
#define mp std::make_pair
std::priority_queue<pii, std::vector<pii>, std::greater<pii>> q;
std::bitset<200005> V;
inline void dij()
{
while(!q.empty()) q.pop();
q.push(mp(0, oo)); V = 0;
for(int i = 1; i <= pcnt; i++)
{
while(V[q.top().second] && !q.empty()) q.pop();
if(q.empty()) return;
int id = q.top().second; q.pop(), V[id] = 1;
for(int j = he[id]; j != -1; j = r[j].nxt)
{
int to = r[j].to;
if(dis[to] > dis[id] + r[j].w)
{
dis[to] = dis[id] + r[j].w;
q.push(mp(dis[to], to));
}
}
}
}
std::unordered_map<int, int> tn; int tcnt;
inline void work(int n, int m, int X, int Y, int ty, int i)
{
tn.clear();
vis = pcnt = rcnt = tcnt = 0;
for(int id : f[ty][i])
{
p[++pcnt] = (st){id, 0, id};
tn[id] = ++tcnt;
vis[tcnt] = true;
}
for(int id : f[ty ^ 1][i])
if(!vis[tn[id % m]])
{
p[++pcnt] = (st){id % m, 0, id};
if(!tn[id % m]) tn[id % m] = ++tcnt;
}
for(int j = 2; j <= pcnt; j++)
{
p[j].x = 1ll * X * (p[j].num - p[1].num) % m;
p[j].x = (p[j].x + m) % m;
}
std::sort(p + 1, p + 1 + pcnt);
he[oo] = -1;
for(int j = 1; j < pcnt; j++)
{
he[tn[p[j].num]] = -1, dis[tn[p[j].num]] = 1e18;
Edge_add(oo, tn[p[j].num], p[j].w);
Edge_add(tn[p[j].num], tn[p[j + 1].num], 1ll * (p[j + 1].x - p[j].x) * n);
}
he[tn[p[pcnt].num]] = -1, dis[tn[p[pcnt].num]] = 1e18;
Edge_add(oo, tn[p[pcnt].num], p[pcnt].w);
Edge_add(tn[p[pcnt].num], 1, 1ll * (m - p[pcnt].x) * n);
dij();
for(int j = 1; j < pcnt; j++)
{
if(!vis[tn[p[j].num]]) sum = std::max(sum, dis[tn[p[j].num]]);
if(p[j + 1].x - p[j].x > 1) sum = std::max(sum, dis[tn[p[j].num]] + 1ll * (p[j + 1].x - p[j].x - 1) * n);
}
if(!vis[tn[p[pcnt].num]]) sum = std::max(sum, dis[tn[p[pcnt].num]]);
if(m - p[pcnt].x > 1) sum = std::max(sum, dis[tn[p[pcnt].num]] + 1ll * (m - p[pcnt].x - 1) * n);
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
n = reads(), m = reads(); d = std::__gcd(n, m);
b = reads(); for(int i = 1; i <= b; i++) h1[i] = reads();
g = reads(); for(int i = 1; i <= g; i++) h2[i] = reads();
if(d > b + g) {printf("-1"); return 0;}
if(b + g == n + m) {printf("0"); return 0;}
for(int i = 1; i <= b; i++) f[0][h1[i] % d].emplace_back(h1[i] / d);
for(int i = 1; i <= g; i++) f[1][h2[i] % d].emplace_back(h2[i] / d);
n /= d, m /= d; exgcd(n, m, X, Y);
X = (X % m + m) % m, Y = (Y % n + n) % n;
for(int i = 0; i < d; i++)
{
if(f[0][i].empty() && f[1][i].empty()) {printf("-1"); return 0;}
if(f[0][i].size() == n && f[1][i].size() == m) continue;
sum = 0;
work(n, m, X, Y, 1, i);
work(m, n, Y, X, 0, i);
ans = std::max(ans, sum * d + i);
}
printf("%lld", ans);
return 0;
}