1. 程式人生 > 其它 >2021-7-14 Luisvacson的膜你賽 題解

2021-7-14 Luisvacson的膜你賽 題解

A.直線

顯然對邊平行且鄰邊垂直的四邊形為矩形,所以任意兩條平行的直線搭上與其垂直的兩條平行線就能構成一個矩形。所以:

\[ans=\sum C_{num_n}^{2}\times C_{num_{-\frac{1}{n}}}^{2} \]

其中\(num_i\)表示\(i\)出現的次數

#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>

int n;
int a, b, c, d;

inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

struct Fraction {
    int up;
    int down;
    friend bool operator<(Fraction a, Fraction b) { return a.up < b.up; }
};

Fraction reduction(Fraction res) {
    if (res.down < 0) {
        res.up = -res.up;
        res.down = -res.down;
    }
    if (res.up == 0) {
        res.down = 1;
    } else {
        int d = gcd(abs(res.up), abs(res.down));
        res.up /= d;
        res.down /= d;
    }
    return res;
}

Fraction divide(Fraction a, Fraction b) {
    Fraction res;
    res.up = a.up * b.down;
    res.down = b.up * b.down;
    return reduction(res);
}

map<Fraction, int> t;
map<Fraction, bool> vis;
Fraction arr[2005];
int idx = 0;

signed main() {
    scanf("%d", &n);
    register int i;

    Fraction x;
    for (i = 1; i <= n; ++i) {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        x.up = a, x.down = b;
        x = reduction(x);
        if (!vis[x]) {
            vis[x] = 1;
            arr[++idx] = x;
        }

        ++t[x];
    }

    Fraction st;
    st.up = -1, st.down = 1;

    int ans = 0;
    for (i = 1; i <= idx; ++i) {
        ans += (t[arr[i]] * (t[arr[i]] - 1) >> 1) *
               (t[divide(st, arr[i])] * (t[divide(st, arr[i])] - 1) >> 1);
    }

    printf("%d\n", ans >> 1);//顯然這樣會將答案統計兩次
    return 0;
}

B.魔法師

題目其實就是告訴你\(w_n=p\times w_{n-1}+(p-1)\),讓你求\(w_k\)

顯然暴力遞推的話\(10^{15}\)的範圍無法接受,原本想放的\(10^7\)暴力部分分又炸了,所以我們需要一種更快的方法。

我們設\(q=p-1\),那麼就有:

\[w_n=p\times w_{n-1}+q \]\[=p\times (p\times w_{n-2}+q)+q \]\[=p^2w_{n-2}+(p+1)q \]\[=p^2(p\times w_{n-3}+q)+(p+1)q \]\[=p^3w_{n-3}+(p^2+p+1)q \]\[=\dots\ \dots \]\[=p^{n-1}w_1+q\sum\limits_{i=0}^{n-2}p^i \]

我們設\(S=\sum\limits_{i=0}^{n-2}p^i\)

那麼\(pS=\sum\limits_{i=1}^{n-1}p^i\)

上述兩式相減得:

\[pS-S=\sum\limits_{i=1}^{n-1}p^i-\sum\limits_{i=0}^{n-2}p^i \]\[(p-1)S=p^{n-1}-1 \]\[S=\dfrac{p^{n-1}-1}{p-1} \]

\(S=\dfrac{p^{n-1}-1}{p-1},q=p-1,w_1=m\)帶入原式可得:

\[w_n=p^{n-1}m+\dfrac{p^{n-1}-1}{p-1}\times (p-1) \]\[=(m+1)p^{n-1}-1 \]

這個式子顯然可以用快速冪高速求解

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mod 998244353

int T;
int m, p, k;

inline int power(int a, int b) {
    int ans = 1, base = a;
    while (b) {
        if (b & 1) ans = ans * base % mod;
        base = base * base % mod;
        b >>= 1;
    }

    return ans % mod;
}

inline int solve(int m, int p, int k) {
    return power(p, k - 1) * (m + 1) % mod - 1;
}

signed main() {
    scanf("%lld", &T);
    while (T--) {
        scanf("%lld%lld%lld", &m, &p, &k);
        printf("%lld\n", solve(m, p, k));
    }

    return 0;
}

C.構造數列

本題構造方法不止一種,這裡給出一種較為容易的

顯然對於一組相鄰的\(a_{2i},a_{2i-1}\),若\(a_{2i}<a_{2i-1}\),那麼會對答案產生\(2\times |a_{2i}-a_{2i-1}|\)的貢獻,反之不產生貢獻,所以我們只需要把\(k+1\)放到第一個和第二個數\(1\)搭配即可產生\(2k\)的貢獻,後面按順序輸出保證不產生貢獻即可

#include <bits/stdc++.h>
using namespace std;

int n, k;
signed main()
{
    scanf("%d%d", &n, &k);
    register int i;

    printf("%d ", k + 1);
    for (i = 1; i <= n * 2; ++i) {
        if (i == k + 1)
            continue;
        printf("%d ", i);
    }

    return 0;
}

D.分數

題面換句話說就是給你\(n\)個數,每次從中選擇一個數除以它的一個因數,直到它等於\(1\),求博弈結果。

我們把每個數進行質因數分解,顯然每次操作就相當於從中拿走一些因數,直到不能再拿為止。

舉個例子:

\[72=2^3\times 3^2 \]

每次我們可以從中拿走一些\(2\),拿走一些\(3\),只要最後拿走數量不為零即可

換句話說我們就是從\(2+3=5\)裡面拿走任意數量(不為零),直到不能再拿為止。

我們對每個數都進行這種處理,將其重新定義為每個質因數的指數和,每次操作就是拿走一定數量,題目到此就轉化成為一個經典的nim遊戲,直接求解即可

不知道什麼是nim遊戲?看這裡

#include <bits/stdc++.h>
using namespace std;

class complex {
public:
    inline char gc()
    {
        static const signed IN_LEN = 1 << 18 | 1;
        static char buf[IN_LEN], *s, *t;
        return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
    }
    template <typename _Tp>
    inline complex& operator>>(_Tp& x)
    {
        static char ch, sgn;
        ch = gc(), sgn = 0;
        for (; !isdigit(ch); ch = gc()) {
            if (ch == -1)
                return *this;
            sgn |= ch == '-';
        }
        for (x = 0; isdigit(ch); ch = gc())
            x = x * 10 + (ch ^ '0');
        sgn && (x = -x);
        return *this;
    }
} inc;

int cnt;
bool isPrime[10005];
vector<int> Prime;

void Init(int n)
{
    memset(isPrime, 1, sizeof(isPrime));
    isPrime[1] = false;
    register int i, j;

    for (i = 2; i <= n; ++i) {
        if (isPrime[i]) {
            ++cnt;
            Prime.push_back(i);
        }
        for (j = 0; j < cnt && i * Prime[j] <= n; ++j) {
            isPrime[i * Prime[j]] = false;
            if (!(i % Prime[j]))
                break;
        }
    }
}

int Frac(int x)
{
    int res = 0;
    register int i;

    for (i = 0; i < cnt; ++i) {
        int p = Prime[i];
        if (p > x)
            break;
        while (!(x % p)) {
            x /= p;
            ++res;
        }
    }

    return res;
}

int n;
int a[1000005], v[1000005];
signed main()
{
    inc >> n;
    register int i;

    int maxn = -1 << 30;
    for (i = 1; i <= n; ++i) {
        inc >> a[i];
        maxn = max(maxn, a[i]);
    }

    Init(maxn);
    int ans = 0;
    for (i = 1; i <= n; ++i) {
        v[i] = Frac(a[i]);
        //printf("%d\n", v[i]);
        ans ^= v[i];
    }

    puts(ans ? "Alice" : "Bob");
    return 0;
}