第四节 同余(二)

Contents

二次剩余

洛谷P5491 【模板】二次剩余

题目大意

x^2\equiv N\pmod p 的解。

typedef long long ll;
typedef pair<ll, ll> pll;
#define mp make_pair
namespace Complex {
    ll mod, W, W0;
    struct comp {
        ll x, y;
        comp(ll xx = 0, ll yy = 0) x : xx, y : yy {}
    };
    comp operator * (comp a, comp b) {
        ll tmp1 = (a.x * b.x + a.y * b.y % mod * W) % mod;
        ll tmp2 = (a.x * b.y + a.y * b.x) % mod;
        return comp(tmp1, tmp2);
    }
    ll ksm(ll a, ll b, ll p) {
        ll ans = 1;
        for (ll base = a; b; b >>= 1) {
            if(b & 1) ans = ans * base % p;
            base = base * base % p;
        }
        return ans;
    }
    comp ksm(comp a, ll b, ll p) {
        comp t=comp(1,0);
        while(b) {
            if(b & 1) t = t * a;
            b >>= 1; a = a * a;
        }
        return t;
    }
};

pll surplus(ll x, ll p) {
    using namespace Complex;
    if (!x) return mp(0, 0);
    mod = p;
    if(ksm(x, (mod-1)>>1, mod) == mod-1) return mp(-1, -1);
    while(233) {
        W0 = rand() % mod;
        W = (W0 * W0 % mod - x + mod) % mod;
        if(ksm(W, (mod-1)>>1, mod) == mod-1) break;
    }
    comp a = ksm(comp(W0, 1), (mod+1)>>1, mod);
    return mp(min(a.x, mod-a.x), max(a.x, mod-a.x));
}
// main 函数里面不要忘记 srand((unsigned)time(0));

类欧几里得算法

定义:

\begin{aligned}
f(n)&=\sum^n_{i=0}\left\lfloor\frac{ai+b}{c}\right\rfloor \bmod {998244353}\cr g(n)&=\sum^n_{i=0}i\left\lfloor\frac{ai+b}{c}\right\rfloor \bmod {998244353}\cr h(n)&=\sum^n_{i=0}\left\lfloor\frac{ai+b}{c}\right\rfloor^2 \bmod {998244353}
\end{aligned}

普通类欧

洛谷P5170 类欧几里得算法

题目大意:

给定 n,a,b,c,求上面的 f(n),g(n),h(n)

拆开用:

注:这样写过不了模版题

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int P = 998244353;
int inv2 = 499122177, inv6 = 166374059;

// 特别注意!如果n,a,b,c初值处于long long范围,需要:
// typedef __int128 ll;

ll f(ll a, ll b, ll c, ll n);
ll g(ll a, ll b, ll c, ll n);
ll h(ll a, ll b, ll c, ll n);

ll f(ll a, ll b, ll c, ll n) {
    if(!n) return (b / c);
    if(!a) return (n + 1) * (b / c) % P;
    if(a >= c || b >= c)
        return ((a/c) * n % P * (n+1) % P * inv2 % P
                + (b/c) * (n + 1) % P + f(a % c, b % c, c, n)) % P;
    ll m = (a * n + b) / c;
    return (n * m % P - f(c, c - b - 1, a, m - 1) + P) % P;
}

ll g(ll a, ll b, ll c, ll n) {
    if (n == 0) return 0;
    if (a == 0) return (b / c) * n % P * (n + 1) % P * inv2 % P;
    if (a >= c || b >= c)
        return ((g(a % c, b % c, c, n) + (a / c) * n % P * (n + 1) % P
                * (2 * n + 1) % P * inv6 % P + (b / c) * n % P
                * (n + 1) % P * inv2 % P) % P + P) % P;
    ll m = (a * n + b) / c;
    return ((n * (n + 1) % P * m % P - f(c, c - b - 1, a, m - 1)
            - h(c, c - b - 1, a, m - 1)) % P * inv2 % P + P) % P;
}

ll h(ll a, ll b, ll c, ll n) {
    if (n == 0) return (b / c) * (b / c) % P;
    if (a == 0) return (n + 1) * (b / c) % P * (b / c) % P;
    if (a >= c || b >= c)
        return (((a / c) * (a / c) % P * n % P * (n + 1) % P
                * (2 * n + 1) % P * inv6 % P + (b / c) * (b / c) % P
                * (n + 1) % P + (a / c) * (b / c) % P * n % P
                * (n + 1) % P + h(a % c, b % c, c, n) + 2 * (a / c) % P
                * g(a % c, b % c, c, n) % P + 2 * (b / c) % P
                * f(a % c, b % c, c, n) % P) % P + P) % P;
    ll m = (a * n + b) / c;
    return ((n * m % P * (m + 1) % P - 2 * g(c, c - b - 1, a, m - 1)
            - 2 * f(c, c - b - 1, a, m - 1) - f(a, b, c, n)) % P + P) % P;
}

int main() {
    int T, n, a, b, c;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d%d", &n, &a, &b, &c);
        printf("%lld %lld %lld\n", f(a, b, c, n), h(a, b, c, n), g(a, b, c, n));
    }
    return 0;
}

合起来写:

#include <bits/stdc++.h>
typedef long long ll;
constexpr int P = 998244353;
constexpr ll inv2 = 499122177;
constexpr ll inv6 = 166374059;

ll f(ll a, ll b, ll c, ll n);
ll g(ll a, ll b, ll c, ll n);
ll h(ll a, ll b, ll c, ll n);

struct Query {
    ll f, g, h;
};

Query solve(ll a, ll b, ll c, ll n) {
    Query ans, tmp;
    if (a == 0) {
        ans.f = (n + 1) * (b / c) % P;
        ans.g = (b / c) * n % P * (n + 1) % P * inv2 % P;
        ans.h = (n + 1) * (b / c) % P * (b / c) % P;
    }
    else if (a >= c || b >= c) {
        tmp = solve(a % c, b % c, c, n);
        ans.f = (tmp.f + (a / c) * n % P * (n + 1) % P
                 * inv2 % P + (b / c) * (n + 1) % P) % P;
        ans.g = (tmp.g + (a / c) * n % P * (n + 1) % P
                 * (2 * n + 1) % P * inv6 % P + (b / c)
                 * n % P * (n + 1) % P * inv2 % P) % P;
        ans.h = ((a / c) * (a / c) % P * n % P * (n + 1) % P
                 * (2 * n + 1) % P * inv6 % P + (b / c) * (b / c)
                 % P * (n + 1) % P + (a / c) * (b / c) % P * n % P
                 * (n + 1) % P + tmp.h + 2 * (a / c) % P * tmp.g
                 % P + 2 * (b / c) % P * tmp.f % P) % P;
        return ans;
    }
    else {
        ll m = (a * n + b) / c;
        tmp = solve(c, c - b - 1, a, m - 1);
        ans.f = (n * (m % P) % P - tmp.f) % P;
        ans.g = (n * (n + 1) % P * (m % P) % P
                 - tmp.f - tmp.h) % P * inv2 % P;
        ans.h = (n * (m % P) % P * ((m + 1) % P) % P
                 - 2 * tmp.g - 2 * tmp.f - ans.f) % P;
    }
    (ans.f += P) %= P; (ans.g += P) %= P; (ans.h += P) %= P;
    return ans;
}

int main() {
    int T, n, a, b, c;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d%d", &n, &a, &b, &c);
        Query ans = solve(a, b, c, n);
        printf("%lld %lld %lld\n", ans.f, ans.h, ans.g);
    }
    return 0;
}

例题:2019牛客多校第九场I KM and M

题目大意:

\sum_{k=1}^{N}((kM)\And M) \bmod (10^9+7)

(1 \le N \le 10^{18}, 1 \le M \le 10^{11})

分析:

可以按位做。判断一个数 x 的第 i 位是不是 1 ,设 a=(x>>(i-1))b=((x>>i)<<1) ,则 x 的第 i 位等于 a-b 。又当且仅当 MkM 的第 i 位都是 1 时,kM 在这一位贡献 2^{i-1} 。因此:

ans=\sum_{i=1}^{(1<<(i-1))\leq m}[(m>>(i-1))\And 1]\cdot\left(\sum_{k=1}^{N}\left(\left\lfloor\frac{kM}{2^{i-1}}\right\rfloor-2\left\lfloor\frac{kM}{2^{i}}\right\rfloor\right)\right)\cdot 2^{i-1}

两个求和均可用类欧几里得求。注意分子分母的 a,b,c 不能对 P 取模!因此要用 __int128 类型计算!

#include <bits/stdc++.h>
using namespace std;
typedef __int128 ll;
const int mod = 1e9 + 7;
const int inv2 = 500000004;
long long n, m;

ll pow2[70];
void init() {
    pow2[0] = 1;
    for(int i = 1; i < 70; i++) pow2[i] = pow2[i-1] * 2;
}
ll f(ll a, ll b, ll c, ll n) {
    if(!n) return (b / c);
    if(!a) return (n + 1) * (b / c) % mod;
    if(a >= c || b >= c)
        return ((a/c) * n % mod * (n+1) % mod * inv2 % mod
                + (b/c) * (n+1) % mod + f(a%c, b%c, c, n)) % mod;
    ll m = (a * n + b) / c;
    return (n * m % mod - f(c, c - b - 1, a, m - 1) + mod) % mod;
}
int main() {
    init();
    scanf("%lld%lld", &n, &m);
    int ans = 0;
    for (int i = 1; pow2[i-1] <= m; i++) {
        if(((m >> (i-1)) & 1) == 0) continue;
        ll tmp1 = f(m, 0, pow2[i - 1], n);
        ll tmp2 = f(m, 0, pow2[i], n) * 2 % mod;
        ll tmp3 = pow2[i - 1] % mod;
        ans = (ans + (tmp1 - tmp2 + mod) * tmp3 % mod) % mod;
    }
    printf("%d\n", ans);
    return 0;
}

模 2 意义下的类欧

bool f(ll a, ll b, ll c, ll n) {
    if (!a) return (((n + 1) & (b / c)) & 1) > 0;
    if (a >= c || b >= c) {
        ll temp = (n & 1) ? (n + 1) / 2 * n : n / 2 * (n + 1);
        return ((a / c * temp + (b / c) * (n + 1) + f(a % c, b % c, c, n)) & 1) > 0;
    }
    else {
        ll m = (a * n + b) / c;
        return (((n * m) ^ f(c, c - b - 1, a, m - 1)) & 1) > 0;
    }
}

例题:hdu6275(2017CCPC杭州 Mod, Xor and Everything)

题目大意:

(n \bmod 1) xor (n \bmod 2) xor \cdots xor (n \bmod (n – 1)) xor (n \bmod n)

分析:

我们可以按位做。对于从低到高第 k 位(从 0 开始):

ans=\sum_{i=1}^{n}\left\lfloor \frac{n\bmod i}{2^k}\right\rfloor=\sum_{i=1}^{n}\left\lfloor \frac{n- \left\lfloor\frac{n}{i}\right\rfloor\cdot i}{2^k}\right\rfloor

可以用类欧+整除分块做。注意到式子中含有负号,因此我们继续化简:

\begin{aligned}
ans&=\sum_{u=i}^{j}\left\lfloor \frac{n- \left\lfloor\frac{n}{i}\right\rfloor\cdot u}{2^k}\right\rfloor\cr &=\sum_{u=i-j}^{0}\left\lfloor \frac{n- \left\lfloor\frac{n}{i}\right\rfloor\cdot (u+j)}{2^k}\right\rfloor\cr &=\sum_{u=0}^{j-i}\left\lfloor \frac{n- \left\lfloor\frac{n}{i}\right\rfloor\cdot (-u+j)}{2^k}\right\rfloor\cr &=\sum_{u=0}^{j-i}\left\lfloor \frac{n- j\left\lfloor\frac{n}{i}\right\rfloor+u\left\lfloor\frac{n}{i}\right\rfloor}{2^k}\right\rfloor\cr
\end{aligned}

由于整除分块中,

\left\lfloor\frac{n}{i}\right\rfloor=\left\lfloor\frac{n}{j}\right\rfloor

故:

n- j\left\lfloor\frac{n}{i}\right\rfloor=n\bmod j

这样就转化成了类欧。注意整除分块的过程中,我们可以一次性把所有位都给算完,即每次求一部分的异或和。同时可以对 [1,3\times 10^7] 之内暴力求。

int main(int argc, const char * argv[]) {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%lld", &n);
        ll ans = 0;
        ll x = min(30000000LL, n);
        for (ll i = 1; i <= x; i++) ans ^= n % i;
        for (ll i = x + 1, j; i <= n; i = j + 1) {
            j = n / (n / i);
            ll tmp = 0;
            ll lim = n / i * (j - i) + n % j;
            for (ll k = 1; k <= lim; k <<= 1) {
                tmp += f(n / i, n % j, k, j - i) * k;
            }
            ans ^= tmp;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇