解题报告 两双手
2023-04-18
这是一篇古老的文章(它通常创建于至少 2 年前),其中的内容可能已经过时。

凳子充分地让我切身实地地体会到了平行四边形的不稳定性。做题的时候可以 360° 无死角地把下半身转来转去,有点意思。也有点容易摔(×


A. 两双手

http://222.180.160.110:1024/contest/3506/problem/1

不难发现可以通过解二元一次方程组得到用几个 A 操作和 B 操作才能让横纵坐标分别改变一些特定的值。可惜我没能想到一个经典的容斥 DP 思想:

定义起点为点 \(0\)。定义 \(C(x,y)\) 表示从 \(x\) 点走到 \(y\) 点的方案数(可经过特殊点)。对于特殊点 \(1\sim n\),顺序枚举,对于点 \(i\),定义 \(f_i\) 表示从 \(0\sim i\) 不经过任何除 \(i\) 以外的特殊点的方案数,则 \(f_i=C(0,i)-\sum\limits_{j=1}^{i-1}f_j\times C(j,i)\)。其中 \(\sum\limits_{j=1}^{i-1}f_j\times C(j,i)\) 就是从 \(0\)\(i\),除 \(i\) 以外经过至少 1 个特殊点的方案数。用总方案数减去不合法方案数,则得到合法方案数。

一个细节。我们枚举时,可能会遇到只能从 \(i\) 到达 \(j\),而不能从 \(j\) 到达 \(i\) 的情况。若恰好 \(j\)\(i\) 前面,那么从 \(i\)\(j\) 的路径就会被统计漏。这个时候怎么办呢?对于任意点 \(i\),我们求得从 \(0\to i\) 所需的步数,并以此为关键字从小到大排序,那么我们就能保证,任意一条合法路径都会被我们统计到。

还是没那么难,有点套路。时间复杂度 \(\mathcal O(n^2)\)

namespace XSC062 {
using namespace fastIO;
#define mkp std::make_pair
using pii = std::pair<int, int>;
const int lim = 1e6;
const int maxn = 505;
const int mod = 1e9 + 7;
const int maxm = 1e6 + 5;
struct _ { int x, y, w; };
std::set<pii> t;
_ s[maxn], s1[maxn];
int fac[maxm], f[maxn];
int ex, ey, n1, n, ax, ay, bx, by;
inline int gcd(int x, int y) {
    return y ? gcd(y, x % y) : x;
}
inline int lcm(int x, int y) {
    return x / gcd(x, y) * y;
}
pii Solve(int a, int b, int c,
                int d, int e, int f) {
    int N = 0, M = 0;
    if (!a) {
        if (c % b)
            return mkp(-1, -1);
        M = c / b, f -= M * e;
        if (f % d)
            return mkp(-1, -1);
        N = f / d;
    }
    else if (!b) {
        if (c % a)
            return mkp(-1, -1);
        N = c / a, f -= N * d;
        if (f % e)
            return mkp(-1, -1);
        M = f / e;
    }
    else if (!d) {
        if (f % e)
            return mkp(-1, -1);
        M = f / e, f -= M * b;
        if (c % a)
            return mkp(-1, -1);
        N = c / a;
    }
    else if (!e) {
        if (f % d)
            return mkp(-1, -1);
        N = f / d, c -= N * a;
        if (c % b)
            return mkp(-1, -1);
        M = c / b;
    }
    else {
        int g = lcm(b, e);
        a *= g / b, c *= g / b, b = g;
        d *= g / e, f *= g / e, e = g;
        if ((c - f) % (a - d))
            return mkp(-1, -1);
        N = (c - f) / (a - d), c -= N * a;
        if (c % b)
            return mkp(-1, -1);
        M = c / b;
    }
    return mkp(N, M);
}
inline int qkp(int x, int y) {
    int res = 1;
    while (y) {
        if (y & 1)
            (res *= x) %= mod;
        (x *= x) %= mod;
        y >>= 1;
    }
    return res;
}
inline int inv(int x) {
    return qkp(x, mod - 2);
}
inline int A(int n, int m) {
    return (fac[n] * inv(fac[n - m])) % mod;
}
inline int C(int n, int m) {
    return (A(n, m) * inv(A(m, m))) % mod;
}
inline void Init(void) {
    fac[0] = 1;
    for (int i = 1; i <= lim; ++i)
        fac[i] = (fac[i - 1] * i) % mod;
    return;
}
int main() {
    read(ex), read(ey), read(n1);
    read(ax), read(ay), read(bx), read(by);
    for (int i = 1; i <= n1; ++i) {
        read(s1[i].x), read(s1[i].y);
        pii t = Solve(ax, bx, s1[i].x,
                        ay, by, s1[i].y);
        s1[i].w = t.first + t.second;
    }
    std::sort(s1 + 1, s1 + n1 + 1,
        [&](_ x, _ y) { return x.w < y.w; });
    Init(), f[0] = 1, ++n1;
    s1[n1].x = ex, s1[n1].y = ey;
    for (int i = 1; i <= n1; ++i) {
        pii t = Solve(ax, bx, s1[i].x,
                        ay, by, s1[i].y);
        if (t.first < 0 || t.second < 0) {
            if (i == n1) {
                puts("0");
                return 0;
            }
            continue;
        }
        s[++n] = s1[i];
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j < i; ++j) {
            pii t = Solve(ax, bx, s[i].x
                - s[j].x, ay, by, s[i].y
                - s[j].y);
            if (t.first < 0 || t.second < 0)
                continue;
            (f[i] += (f[j] * C(t.first +
                    t.second, t.first)) %
                    mod) %= mod;
        }
        pii t = Solve(ax, bx, s[i].x,
                    ay, by, s[i].y);
        f[i] = (C(t.first + t.second,
                t.first) + mod - f[i]) % mod;
    }
    print(f[n]);
    return 0;
}
} // namespace XSC062

言论