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;
}
posted @ 2022-06-11 14:52  zuytong  阅读(26)  评论(0编辑  收藏  举报