闲话 22.10.8
闲话
突然感觉好久没翻译东西了
所以拿今天这题的题面复健一下
果然还是在翻译的时候感觉很爽
好的今天又来了一道SA模板
然而我仍然不会
扔了个 \(O(n^2\log n)\) 的排序上去,随便写了个很丑的暴力lcp匹配
90pts
优化了lcp的匹配
100pts
我不理解 这数据真就拿脚造呗
被推荐了一道题
[省选联考 2021 B 卷] 取模
“在已知CCF连续两年D1T1的数据是拿脚随的的情况下”是这么说的
“我们充分发扬人类智慧”是这么说的
“根据数学直觉,取最大 6 个点匹配,”是这么A的
我不理解 这数据真就拿脚造呗
分断を生んじゃった椅子取りゲーム 無痛分娩で授かるベイブ
壮大な内輪ノリを歴史と呼ぶ
生きたいが死ねと言われ 死にたいが生きろと言われ
生きたいが死ねと言われ 死にたいが生きろと言われ
幸せ自慢はダメ? 不幸嘆いてもダメ?
杂题
拉琪旭喜欢拍电影,所以奈芙莲帮她开了一家电影院。我们可以叫它 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)\) 的所有操作序列。根据卡特兰数结论,容易发现答案是
证明
由于从 \((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)\) 的路径数,即
证毕。
因此答案即为
注意由于 \(\frac{i+j}2\) 为 \(1\) 的个数,为整数,因此需要使得 \(i,j\) 的奇偶性相同。
我们发现后面那部分的求和号可以展开使得每两项之间正负和为 \(0\),于是可以展开。记第一个与 \(i\) 奇偶性相同的 \(j\) 为 \(j_l\),最后一个为 \(j_r\),立得
于是可以在 \(O(n)\) 复杂度内算得答案。
但是我们是不是忘记了点什么……\(p\) 不是质数啊(笑
根据唯一分解定理,记
显然有 \(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;
}
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat221008.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。