Loading [MathJax]/extensions/tex2jax.js

P5330:「SNOI2019」数论 – 题解

主要思路

复盘一年前做过的题,现在感觉确实简单。

考虑表示成 \(d’ \in A, d = d’ + xP\),然后算有多少个 \(x\) 满足 \(d\) 在 \(B\) 中。暴力直接枚举。考虑优化暴力,我们发现这个肯定是有个循环节的,长度为 \(\text{lcm}(P, Q)\),这个可以做一个轻微的优化。

我们考虑把这个模 \(Q\) 想成一个环形带,然后把起始位置为 \(start_pos\) 的 \(d \in B\) 在这个纸带上进行标记、做前缀和,为了方便判断前缀和的加减顺序,我们还要标记排名。

计算的时候,对于 \(A_i\),我们需要知道这个元素 \(T\) 经历了多少次(\(cnt\))循环,然后完整循环的总和算进去。算完这个之后,我们来算开头和结尾。开头位置肯定就是 \(A_i \bmod Q\),结尾则是 \((A_i + cnt * P \bmod loop) \bmod Q\)。然后前缀和搞下即可。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// P5330.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200;
typedef long long ll;
int P, Q, n, m, A[MAX_N], B[MAX_N], prefix[MAX_N], rnk[MAX_N], tot[MAX_N], key[MAX_N];
bool vis[MAX_N];
ll T;
int main()
{
scanf("%d%d%d%d%lld", &P, &Q, &n, &m, &T);
int len = Q / __gcd(P, Q);
for (int i = 1; i <= n; i++)
scanf("%d", &A[i]);
for (int i = 1; i <= m; i++)
scanf("%d", &B[i]), prefix[B[i]] = 1, key[B[i]] = 1;
for (int start_pos = 0; start_pos < Q; start_pos++)
{
if (vis[start_pos])
continue;
int last_pos = start_pos;
rnk[last_pos] = 1, vis[start_pos] = true;
for (int curt = (start_pos + P) % Q; curt != start_pos; last_pos = curt, curt = (curt + P) % Q)
vis[curt] = true, rnk[curt] = rnk[last_pos] + 1, prefix[curt] += prefix[last_pos];
tot[start_pos] = prefix[last_pos];
for (int curt = (start_pos + P) % Q; curt != start_pos; curt = (curt + P) % Q)
tot[curt] = prefix[last_pos];
}
ll ans = 0;
for (int i = 1; i <= n; i++)
{
if (A[i] >= T)
continue;
ll loop_cnt = (T - A[i] - 1) / P;
ll lft = A[i] % Q, rig = (A[i] + loop_cnt % len * P) % Q;
ans += loop_cnt / len * tot[lft];
if (rnk[rig] >= rnk[lft])
ans += prefix[rig] - prefix[lft] + key[lft];
else
ans += tot[lft] - (prefix[lft] - prefix[rig] - key[lft]);
}
printf("%lld\n", ans);
return 0;
}
// P5330.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e6 + 200; typedef long long ll; int P, Q, n, m, A[MAX_N], B[MAX_N], prefix[MAX_N], rnk[MAX_N], tot[MAX_N], key[MAX_N]; bool vis[MAX_N]; ll T; int main() { scanf("%d%d%d%d%lld", &P, &Q, &n, &m, &T); int len = Q / __gcd(P, Q); for (int i = 1; i <= n; i++) scanf("%d", &A[i]); for (int i = 1; i <= m; i++) scanf("%d", &B[i]), prefix[B[i]] = 1, key[B[i]] = 1; for (int start_pos = 0; start_pos < Q; start_pos++) { if (vis[start_pos]) continue; int last_pos = start_pos; rnk[last_pos] = 1, vis[start_pos] = true; for (int curt = (start_pos + P) % Q; curt != start_pos; last_pos = curt, curt = (curt + P) % Q) vis[curt] = true, rnk[curt] = rnk[last_pos] + 1, prefix[curt] += prefix[last_pos]; tot[start_pos] = prefix[last_pos]; for (int curt = (start_pos + P) % Q; curt != start_pos; curt = (curt + P) % Q) tot[curt] = prefix[last_pos]; } ll ans = 0; for (int i = 1; i <= n; i++) { if (A[i] >= T) continue; ll loop_cnt = (T - A[i] - 1) / P; ll lft = A[i] % Q, rig = (A[i] + loop_cnt % len * P) % Q; ans += loop_cnt / len * tot[lft]; if (rnk[rig] >= rnk[lft]) ans += prefix[rig] - prefix[lft] + key[lft]; else ans += tot[lft] - (prefix[lft] - prefix[rig] - key[lft]); } printf("%lld\n", ans); return 0; }
// P5330.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e6 + 200;

typedef long long ll;

int P, Q, n, m, A[MAX_N], B[MAX_N], prefix[MAX_N], rnk[MAX_N], tot[MAX_N], key[MAX_N];
bool vis[MAX_N];
ll T;

int main()
{
    scanf("%d%d%d%d%lld", &P, &Q, &n, &m, &T);
    int len = Q / __gcd(P, Q);
    for (int i = 1; i <= n; i++)
        scanf("%d", &A[i]);
    for (int i = 1; i <= m; i++)
        scanf("%d", &B[i]), prefix[B[i]] = 1, key[B[i]] = 1;
    for (int start_pos = 0; start_pos < Q; start_pos++)
    {
        if (vis[start_pos])
            continue;
        int last_pos = start_pos;
        rnk[last_pos] = 1, vis[start_pos] = true;
        for (int curt = (start_pos + P) % Q; curt != start_pos; last_pos = curt, curt = (curt + P) % Q)
            vis[curt] = true, rnk[curt] = rnk[last_pos] + 1, prefix[curt] += prefix[last_pos];
        tot[start_pos] = prefix[last_pos];
        for (int curt = (start_pos + P) % Q; curt != start_pos; curt = (curt + P) % Q)
            tot[curt] = prefix[last_pos];
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        if (A[i] >= T)
            continue;
        ll loop_cnt = (T - A[i] - 1) / P;
        ll lft = A[i] % Q, rig = (A[i] + loop_cnt % len * P) % Q;
        ans += loop_cnt / len * tot[lft];
        if (rnk[rig] >= rnk[lft])
            ans += prefix[rig] - prefix[lft] + key[lft];
        else
            ans += tot[lft] - (prefix[lft] - prefix[rig] - key[lft]);
    }
    printf("%lld\n", ans);
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *