「Fortuna OJ」Aug 4th – Group A 解题报告

今天被各路神仙吊打,顺利 gg。

A – Forging 锻造

在考场上我推了个有后效性的 DP,还以为是高斯消元,然后看到数据范围就 GG 了。这道题主要是把一些东西给转换掉了。

首先,对一个概率为\(p\)的事件,它的期望次数是\(\frac{1}{p}\)(参见这里)。然后,考虑一个状态\(dp[i]\)代表合成第\(i\)级武器的期望花费。显然\(dp[0] = a\)。

考虑\(dp[1]\)的求法,首先需要两个级别为\(0\)的武器,然后发现无论失败与否都可以保证至少又一个\(0\)级武器,所以我们只需要注意另外一个武器的花费就可以了:

\[ dp[1] = dp[0] + dp[1] \times \frac{1}{p} \]

也就是说,第一件武器的贡献在任何概率下都存在,即使失败也会至少存在一个武器;对于另外一件武器,考虑乘上它成功的次数就行了。

我们现在考虑\(i \geq 2\)的情况。首先,\(i – 2\)级别的武器永远存在,所以不需要乘系数,\(i – 1\)级别的武器锻造成功的次数也是\(\frac{1}{p}\),所以:

\[ dp[i] = dp[i – 1] * \frac{1}{p} + dp[i – 2] \]

这道题就没了,现在感觉有点傻逼。

// A.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e7 + 20, mod = 998244353;

int b[MAX_N], c[MAX_N], n, a, bx, by, cx, cy, p, inv[MAX_N], max_range, dp[MAX_N];

int main()
{
    freopen("forging.in", "r", stdin);
    freopen("forging.out", "w", stdout);
    scanf("%d%d%d%d%d%d%d", &n, &a, &bx, &by, &cx, &cy, &p);
    b[0] = by + 1, max_range = max(max_range, b[0]);
    c[0] = cy + 1, max_range = max(max_range, c[0]);
    for (int i = 1; i < n; i++)
    {
        b[i] = ((long long)b[i - 1] * bx + by) % p + 1, max_range = max(max_range, b[i]);
        c[i] = ((long long)c[i - 1] * cx + cy) % p + 1, max_range = max(max_range, c[i]);
    }

    inv[1] = 1;
    for (int i = 2; i <= max_range; i++)
        inv[i] = (long long)(mod - mod / i) * inv[mod % i] % mod;

    dp[0] = a;
    dp[1] = 1LL * (a + 1LL * inv[min(b[0], c[0])] * c[0] % mod * a % mod) % mod;
    for (int i = 2; i <= n; i++)
        dp[i] = (1LL * dp[i - 1] * inv[min(b[i - 2], c[i - 1])] % mod * c[i - 1] % mod + dp[i - 2]) % mod;
    printf("%d", dp[n]);
    return 0;
} // A.cpp

B – Division 整除

他直接给了你\(n\)的单次质因子,所以会想到 CRT。考虑在各个素数域内进行线性筛来筛积性函数\(x^m\),然后统计\(x^m \equiv x \ (mod p_i)\)的个数,然后把答案乘起来就可以了。

// B.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 55, mod = 998244353;

int id, T, c, ci[MAX_N], m, func[12000], primes[12000];
bool isPrime[10001];

inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

inline int quick_pow(int bas, int tim, int modulo)
{
    int ans = 1;
    bas %= modulo;
    while (tim)
    {
        if (tim & 1)
            ans = 1LL * ans * bas % modulo;
        bas = 1LL * bas * bas % modulo;
        tim >>= 1;
    }
    return ans;
}

inline int sieve(int n)
{
    int ans = 2;
    for (register int i = 2; i <= n - 1; i++)
    {
        if (!isPrime[i])
            func[i] = quick_pow(i, m, n);
        for (register int j = 1; j <= n - 1; j++)
        {
            if (i * primes[j] > n)
                break;
            func[i * primes[j]] = func[i] * func[primes[j]] % n;
            if (i % primes[j] == 0)
                break;
        }
        ans += func[i] == i;
    }
    return ans;
}

int main()
{
    /*
    freopen("division.in", "r", stdin);
    freopen("division.out", "w", stdout);
    */

    int tot = 0;
    for (register int i = 2; i <= 10000; i++)
    {
        if (!isPrime[i])
            primes[++tot] = i;
        for (register int j = 1; j <= tot; j++)
        {
            if (i * primes[j] > 10000)
                break;
            isPrime[i * primes[j]] = true;
            if (i % primes[j] == 0)
                break;
        }
    }

    id = read(), T = read();
    while (T--)
    {
        int ans = 1;
        c = read(), m = read();
        for (register int i = 1; i <= c; i++)
            ci[i] = read(), ans = 1LL * ans * sieve(ci[i]) % mod;
        printf("%d\n", ans);
    }
    return 0;
} // B.cpp

C – Money 欠钱

这道题大力 LCT 就行了。中间要注意只有深度严格单调的链才能被统计,且必须注意进行split(x, y)操作时要搞清楚这条链的朝向。

// C.cpp
#include <bits/stdc++.h>
#pragma GCC target("avx")
#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
#define lson ch[p][0]
#define rson ch[p][1]

using namespace std;

const int MAX_N = 1e5 + 200, INF = 0x3f3f3f3f;

int ch[MAX_N][2], fa[MAX_N], val[MAX_N], minVal[MAX_N], revTag[MAX_N];
int size[MAX_N], n, m, mem[MAX_N], sta[MAX_N];

#define rint register int

void write(int x){
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

inline char nc()
{
    static char buf[100001], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline int read()
{
    rint num = 0;
    char c;
    while (isspace(c = nc()))
        ;
    while (num = num * 10 + c - 48, isdigit(c = nc()))
        ;
    return num;
}

inline bool isRoot(rint p)
{
    return ch[fa[p]][0] != p && ch[fa[p]][1] != p;
}

inline int check(rint p) { return ch[fa[p]][1] == p; }

inline void clear(rint p) { lson = rson = fa[p] = val[p] = size[p] = revTag[p] = minVal[p] = 0; }

inline void pushUp(rint p)
{
    clear(0);
    size[p] = 1 + size[lson] + size[rson];
    minVal[p] = val[p];
    if (lson)
        minVal[p] = min(minVal[p], minVal[lson]);
    if (rson)
        minVal[p] = min(minVal[p], minVal[rson]);
}

inline void pushDown(rint p)
{
    if (revTag[p])
    {
        if (lson)
            revTag[lson] ^= 1, swap(ch[lson][0], ch[lson][1]);
        if (rson)
            revTag[rson] ^= 1, swap(ch[rson][0], ch[rson][1]);
        revTag[p] = 0;
    }
}

inline void update(rint x)
{
    rint tot = 0;
    sta[++tot] = x;
    while (!isRoot(x))
        x = fa[x], sta[++tot] = x;
    while (tot)
        pushDown(sta[tot--]);
}

inline void rotate(rint x)
{
    rint y = fa[x], z = fa[y], dir = check(x), w = ch[x][dir ^ 1];
    fa[x] = z;
    if (!isRoot(y))
        ch[z][check(y)] = x;
    ch[y][dir] = w, fa[w] = y;
    ch[x][dir ^ 1] = y, fa[y] = x;
    pushUp(y), pushUp(x), pushUp(z);
}

inline void splay(rint x)
{
    update(x);
    for (rint fat = fa[x]; fat = fa[x], !isRoot(x); rotate(x))
        if (!isRoot(fat))
            rotate(check(fat) == check(x) ? fat : x);
}

inline int access(rint x)
{
    rint fat = 0;
    for (; x != 0; fat = x, x = fa[x])
        splay(x), ch[x][1] = fat, pushUp(x);
    return fat;
}

inline void makeRoot(rint p)
{
    access(p), splay(p);
    swap(lson, rson), revTag[p] ^= 1;
}

inline int find(rint p)
{
    access(p), splay(p);
    while (lson)
        p = lson;
    splay(p);
    return p;
}

inline void link(rint x, rint y)
{
    makeRoot(x);
    if (find(y) == x)
        return;
    fa[x] = y;
}

inline void split(rint x, rint y)
{
    makeRoot(x);
    access(y), splay(y);
}

inline int lca(rint x, rint y)
{
    access(x);
    return access(y);
}

inline int findFa(rint x) { return mem[x] == x ? x : mem[x] = findFa(mem[x]); }

int main()
{
    /*
    freopen("money.in", "r", stdin);
    freopen("money.out", "w", stdout);
    */

    rint lastans = 0;
    n = read(), m = read();
    for (rint i = 1; i <= n; i++)
        mem[i] = i, val[i] = minVal[i] = INF;
    while (m--)
    {
        rint opt, a, b, c;
        opt = read(), a = read(), b = read();
        a = (a + lastans) % n + 1;
        b = (b + lastans) % n + 1;
        if (opt == 0)
        {
            c = read(), c = (c + lastans) % n + 1;
            mem[findFa(a)] = findFa(b);
            val[a] = minVal[a] = c;
            link(a, b);
        }
        else
        {
            if (findFa(a) != findFa(b))
            {
                write(lastans = 0), puts("");
                continue;
            }
            if (lca(a, b) == b)
            {
                rint root = findFa(b);
                split(a, b);
                write(lastans = minVal[ch[b][0]]), puts("");
                makeRoot(root);
            }
            else
                write(lastans = 0), puts("");
        }
    }
    return 0;
}

 

Leave a Reply

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