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;
}

posted @ 2021-08-16 20:33  Luisvacson  阅读(72)  评论(0)    收藏  举报