闲话 22.10.8

闲话

突然感觉好久没翻译东西了
所以拿今天这题的题面复健一下
果然还是在翻译的时候感觉很爽

好的今天又来了一道SA模板
然而我仍然不会
扔了个 \(O(n^2\log n)\) 的排序上去,随便写了个很丑的暴力lcp匹配
90pts
优化了lcp的匹配
100pts
我不理解 这数据真就拿脚造呗

被推荐了一道题
[省选联考 2021 B 卷] 取模
“在已知CCF连续两年D1T1的数据是拿脚随的的情况下”是这么说的
“我们充分发扬人类智慧”是这么说的
“根据数学直觉,取最大 6 个点匹配,”是这么A的
我不理解 这数据真就拿脚造呗

分断を生んじゃった椅子取りゲーム 無痛分娩で授かるベイブ

壮大な内輪ノリを歴史と呼ぶ

生きたいが死ねと言われ 死にたいが生きろと言われ

生きたいが死ねと言われ 死にたいが生きろと言われ

幸せ自慢はダメ? 不幸嘆いてもダメ?

杂题

CF896D

拉琪旭喜欢拍电影,所以奈芙莲帮她开了一家电影院。我们可以叫它 No.68 电影院。

然而,有一天,No.68 电影院没有零钱了(她们当前没有 50 元钞票),但是奈芙莲仍然想要把电影院开下去。(这里假定“元”是悬浮大陆群的一种货币。)

现在有三种顾客:第一种带来了 50 元钞票,刚好够买一张电影票;第二种带来了 100 元钞票,而奈芙莲需要找给他/她 50 元钞票零钱;最后一种有 VIP 卡,所以她们不需要花钱。

现在有 \(n\) 个顾客在电影院门口排队。奈芙莲想知道有多少种可能的排队方案,使得他们能被顺序接待(即所有顾客都能得到找零),而且接待完所有顾客后剩余的 50 元钞票数量在 \([l,r]\) 之间。两种排队方案不同当且仅当存在一个顾客在两种方案中的种类不同。由于答案可能很大,请输出对 \(p\) 取模的值。

\(0\le l \le r \le n \le 10^5, p \le 2\times 10^9\)保证 \(p\) 是质数。

组合题。
抽象题面,序列计数,元素取值 \(\in \{1,0,-1\}\),满足任意位置前缀和非负,总和 \(\in [l,r]\)

首先考虑 \(p\) 是质数的时候怎么做。这时我们发现可以预处理阶乘来算组合数了。
考虑答案怎么算。

我们发现 \(0\) 对答案没用,于是可以枚举 \(0\) 的个数 \(c_0\),算出剩下的 \((n-c_0)\) 个数的答案后乘入 \(\large\binom{n}{c0}\) 即可。然后考虑剩下的取值 \(\in \{1,-1\}\) 的元素怎么算。
设非 \(0\) 数的数量为 \(i\)\(1\) 的数量与 \(-1\) 的数量之差(答案)为 \(j\),则立得 \(1\) 的数量为 \(\frac{i+j}2\)\(-1\) 的数量为 \(\frac{i-j}2\)
抽象操作。我们设序列是二维格点上的操作序列,\(1\) 为向左走 \(1\) 距离,\(-1\) 为向上走 \(1\),则答案即为不越过 \(y=x\),从 \((0,0)\)\((\frac{i+j}2,\frac{i-j}2)\) 的所有操作序列。根据卡特兰数结论,容易发现答案是

\[\binom{i}{\frac{i+j}2} - \binom{i}{\frac{i+j}2+1} \]

证明

由于从 \((0,0)\)\((\frac{i+j}2,\frac{i-j}2)\) 的所有操作序列数量为 \(\binom{i}{\frac{i+j}2}\),只需证明越过 \(y=x\),从 \((0,0)\)\((\frac{i+j}2,\frac{i-j}2)\) 的所有操作序列数量为 \(\binom{i}{\frac{i+j}2+1}\) 即可。

由于这玩意是卡特兰相关,考虑一个对称。
我们发现,越过 \(y=x\) 的路径定会交 \(l:y=x+1\) 于至少一个点。考虑将交点及之后的所有部分路径关于 \(l\) 作对称,则我们得到了一系列到达 \((\frac{i+j}2,\frac{i-j}2)\) 关于 \(l\) 的对称点 \((\frac{i+j}2+1,\frac{i-j}2-1)\) 的路径。

容易发现对于所有不合法的路径,一定可以被包含在交 \(l\) 的路径上,而合法路径一定不会被包含在其中。于是对不合法路径的计数即为自 \((0,0)\)\((\frac{i+j}2+1,\frac{i-j}2-1)\) 的路径数,即

\[\binom{i}{\frac{i+j}2+1} \]

证毕。

因此答案即为

\[\sum_{i=0}^n \binom{n}{i} \left(\sum_{j=l}^r\binom{i}{\frac{i+j}2} - \binom{i}{\frac{i+j}2+1}\right) \]

注意由于 \(\frac{i+j}2\)\(1\) 的个数,为整数,因此需要使得 \(i,j\) 的奇偶性相同。

我们发现后面那部分的求和号可以展开使得每两项之间正负和为 \(0\),于是可以展开。记第一个与 \(i\) 奇偶性相同的 \(j\)\(j_l\),最后一个为 \(j_r\),立得

\[\sum_{i=0}^n \binom{n}{i} \left(\binom{i}{\frac{i+j_l}2} - \binom{i}{\frac{i+j_r}2+1}\right) \]

于是可以在 \(O(n)\) 复杂度内算得答案。

但是我们是不是忘记了点什么……\(p\) 不是质数啊(笑

根据唯一分解定理,记

\[p = \prod_{i=1}^{k}p_i^{c_i},\forall c_i > 0 \]

显然有 \(k\le 9\)

对于每个数 \(x\),我们都可以将其表为 \(x\times p_{i}^{\alpha_i}\),其中 \(x\perp p\)\(\alpha_i \ge 0\)
于是在这种表示法下,乘除法是简便的:乘法只需要将互质部分相乘再模 \(p\)\(p\) 质因子的指数相加;逆元可以通过欧拉定理得到,指数相减。然而加减法是不得当的,但是我们不用加减法。

预处理每个数字的表示法,在计算时按正常公式计算阶乘与组合数即可。
获取答案时直接使用快速幂乘入质数部分即可。

此方法的复杂度是 \(O(nk)\) 的,由于 \(k\le 9\),因此可以快速得到答案。
使用光速幂做到 \(O(1)\) 计算,我们有最坏时间复杂度 \(O(n \log p)\)

code
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for (register int (i) = (a); (i) <= (b); ++(i))
#define pre(i,a,b) for (register int (i) = (a); (i) >= (b); --(i))
const int N = 1e5 + 10;
int n, mod, ans, l, r, t1, t2;
int phi, phi_1, fact[15], tot[15], cnt;

typedef long long ll; typedef __int128 lll;
struct FastMod { int m; ll b; void init(int _m) { m = _m; b = ((lll)1<<64) / m; } int operator() (ll a) {ll q = ((lll)a * b) >> 64; a -= q * m; if (a >= m) a -= m; return a; } } Mod;
int add(ll a, int b) { return (a += b) >= mod ? a - mod : a; } int mul(int a, int b) { return Mod(1ll * a * b); } template <typename ...Args> int mul(int a, Args ...b) { return mul(a, mul(b...)); }

int qp(int a, int b) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = mul(a, ret);
        a = mul(a, a);
        b >>= 1;
    } return ret;
}

struct Light {
    int p, c, sq, dsq[1000], usq[1000];
    void init(int _p, int _c) {
        p = _p, c = _c;
        sq = sqrt(_c) + 1;
        dsq[0] = usq[0] = 1;
        rep(i,1,sq-1) dsq[i] = mul(dsq[i-1], p);
        usq[1] = mul(dsq[sq-1], p);
        rep(i,2,sq) usq[i] = mul(usq[i], usq[1]);
    }
    int qp(int c) {
        return mul(dsq[c % sq], usq[c / sq]);
    }
} qpow[15];

struct Int {
    int arg, inv, c[15];

    Int() { arg = inv = 1; memset(c, 0, sizeof c); }

    Int(int k) {
        register int d;
        rep(i,1,cnt) {
            d = k / fact[i];
            c[i] = 0;
            while (d * fact[i] == k) c[i]++, k = d, d = k / fact[i];
            tot[i] = max(tot[i], c[i]);
        } k %= mod;
        arg = k; inv = qp(arg, phi_1);
    }

    Int operator = (const Int & a) {
        arg = a.arg, inv = a.inv;
        rep(i,1,cnt) c[i] = a.c[i];
        return *this;
    } 

    Int operator * (const Int & b) const {
        register Int ret;
        ret.arg = mul(arg, b.arg);
        ret.inv = mul(inv, b.inv);
        rep(i,1,cnt) ret.c[i] = c[i] + b.c[i], tot[i] = max(tot[i], ret.c[i]);
        return ret;
    }

    Int operator / (const Int & b) const {
        register Int ret;
        ret.arg = mul(arg, b.inv);
        ret.inv = mul(inv, b.arg);
        rep(i,1,cnt) ret.c[i] = c[i] - b.c[i];
        return ret;
    }

    int get_val() {
        register int ret = arg;
        rep(i,1,cnt) ret = mul(ret, qpow[i].qp(c[i]));
        return ret;
    }
} Factor[N];

int C(int a, int b) {
    if (a < b) return 0;
    return (Factor[a] / Factor[b] / Factor[a - b]).get_val();
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> mod >> l >> r;
    Mod.init(mod);
    t1 = mod, phi = mod;
    for (register int i = 2; i * i <= t1; i++) {
        if (t1 % i == 0) {
            fact[++cnt] = i;
            phi = phi / i * (i-1);
            while (t1 % i == 0) t1 /= i;
        } 
    } if (t1 > 1) fact[++cnt] = t1, phi = phi / t1 * (t1 - 1);
    phi_1 = phi - 1;
    Factor[0] = Int(1);
    rep(i,1,n) Factor[i] = Factor[i-1] * Int(i);
    register int ret = 0, lb, rb, tans;
    rep(i,1,cnt) qpow[i].init(fact[i], tot[i]);
    rep(i,l,n) {
        lb = l;
        ((lb^i) & 1) ? (++lb) : (0);
        rb = min(i, r);
        ((lb^i) & 1) ? (--lb) : (0);
        if (lb > rb) continue;
        tans = add(C(i, (i + lb) >> 1), mod - C(i, (i + rb + 2) >> 1));
        tans = mul(tans, C(n, i));
        ans = add(ans, tans);
    } cout << ans << endl;
}
posted @ 2022-10-08 21:04  joke3579  阅读(70)  评论(2编辑  收藏  举报