2023.02.20省选组模拟总结有哪些亮点?

摘要:$text{summary}$ 又是写暴力的一天 $text{T1【USACO 2020 US Open Platinum】 Exercise}$ 发现自己连 $dp$ 都不会了 考场暴力都写不出来的原因:不知如何计算各个环长确定的情况
\(\text{summary}\) 又是写暴力的一天 \(\text{T1【USACO 2020 US Open Platinum】 Exercise}\) 发现自己连 \(dp\) 都不会了 考场暴力都写不出来的原因:不知如何计算各个环长确定的情况下对应排列的数目 因为没有逆元,没法直接排列组合计数。。。 关键在于如果存在环长相同的情况,直接计数不可避免要除掉相同环长数量的阶乘 然而可以 \(dp\) 计数,考场想了一会却不会 考虑一个环的代表为这个环上最小的数,那么枚举当前最小值所在环的大小转移,这样就可以避免算重了 这道题又是 \(\text{lcm}\) 相关的计数 考虑单独拆开算每个质因子的指数,对某个质因子而言计算 \(lcm=p^c\) 的排列数量 改成计算 \(p^c|lcm\) 的排列数量,那么环中只要至少存在一个 \(p^c\) 倍数的环即可 补集思想,计数不存在一个环长为 \(p^c\) 倍数的排列数 若长度为 \(i\),这个数目记为 \(f_i\),那么 \(f_i=i!-\sum_{p^c|j} f_j g_{i-j}\) \(g_i\) 表示长度为 \(i\) 的排列环长均为 \(p^c\) 的倍数的排列数目 于是就可以 \(O(n^2) \texttt{ } dp\) 了 \(\texttt{warning:}\) 加法运算整个式子的类型默认为第一个对象的类型 \(\text{Code}\) $\text{Code}$ #include <bits/stdc++.h> #define IN inline using namespace std; typedef long long LL; const int N = 7505; int b[N], n, M, P, C[N][N], bz[N]; IN void Add(LL &x, int y){if ((x += y) >= P) x -= P;} int fpow(int x, int y, int p) { int s = 1; for(; y; y >>= 1, x = (LL)x * x % p) if (y & 1) s = (LL)s * x % p; return s; } LL f[N], g[N], fac[N]; IN int calc(int x) { g[0] = 1; for(int i = x; i <= n; i += x) { g[i] = 0; for(int j = x; j <= i; j += x) Add(g[i], g[i - j] * C[i - 1][j - 1] % P * fac[j - 1] % P); } f[0] = 1; for(int i = n % x; i <= n; i += x) { f[i] = fac[i]; for(int j = x; j <= i; j += x) Add(f[i], P - g[j] * C[i][j] % P * f[i - j] % P); } return (fac[n] - f[n] + P) % P; } int main() { freopen("exercise.in", "r", stdin); freopen("exercise.out", "w", stdout); scanf("%d%d", &n, &M), P = M - 1, fac[0] = 1; for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % P; for(int i = 0; i <= n; i++) C[i][0] = 1; for(int i = 1; i <= n; i++) for(int j = 1; j <= i; j++) { LL z = C[i - 1][j]; Add(z, C[i - 1][j - 1]); C[i][j] = z; } int ans = 1; for(int i = 2; i <= n; i++) if (!bz[i]) { for(int j = i * i; j <= n; j += i) bz[j] = 1; for(int j = i; j <= n; j *= i) ans = (LL)ans * fpow(i, calc(j), M) % M; } printf("%d\n", ans); } \(\text{T2 6517. 【GDOI2020模拟03.28】数三数(three)}\) 仙姑了。。。 \(\text{T3 6568. 【GDOI2020模拟】 迫害 DJ (hakugai)}\) \(g\) 在 \(p\) 意义下是有个循环节的,且比较短,和斐波那契数列循环节一样 找到这个循环节即可,然后就是套路矩乘了 \(\texttt{warning}:\) 函数定义的 \(void\) 不能漏,本地编译却可通过 \(\text{Code}\) $\text{Code}$ #include <bits/stdc++.h> #define IN inline using namespace std; template <typename Tp> IN void read(Tp &x) { x = 0; char ch = getchar(); int f = 0; for(; !isdigit(ch); f = (ch == '-' ? 1 : f), ch = getchar()); for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar()); if (f) x = ~x + 1; } typedef long long LL; LL P, pk[105]; LL gcd(LL x, LL y){return (!y ? x : gcd(y, x % y));} struct Matrix { LL s00, s01, s10, s11; IN Matrix(LL a0 = 0, LL a1 = 0, LL a2 = 0, LL a3 = 0){s00 = a0, s01 = a1, s10 = a2, s11 = a3;} IN Matrix operator * (const Matrix &B) { Matrix C; C.s00 = (s00 * B.s00 % P + s01 * B.s10 % P) % P; C.s01 = (s00 * B.s01 % P + s01 * B.s11 % P) % P; C.s10 = (s10 * B.s00 % P + s11 * B.s10 % P) % P; C.s11 = (s10 * B.s01 % P + s11 * B.s11 % P) % P; return C; } }Z, I, B; IN Matrix fpow(Matrix x, LL y) { Matrix s = I; for(; y; y >>= 1, x = x * x) if (y & 1) s = s * x; return s; } IN int find(int x) { if (x == 2) return 3; if (x == 3) return 4; if (x == 5) return 10; if (x % 5 == 1 || x % 5 == 4) return x - 1; return x + 1; } IN LL calc(LL p) { LL lcm = 1; for(LL i = 2; i * i <= p; i++) if (p % i == 0) { LL w = 1; while (p % i == 0) p /= i, w *= i; w /= i, w = w * find(i), lcm = lcm / gcd(lcm, w) * w; } if (p > 1) p = find(p), lcm = lcm / gcd(lcm, p) * p; return lcm; } int main() { freopen("hakugai.in", "r", stdin); freopen("hakugai.out", "w", stdout); int t; read(t); I = Matrix(1, 0, 0, 1); for(; t; --t) { int a, b, p, k; LL n; read(a), read(b), read(n), read(k), read(p); pk[1] = p; for(int i = 2; i <= k; i++) pk[i] = calc(pk[i - 1]); for(Matrix ret; k; k--) { P = pk[k], Z = Matrix(a % P, b % P, 0, 0), B = Matrix(0, P - 1, 1, 3); ret = Z * fpow(B, n), n = ret.s00; } printf("%lld\n", n); } } 这个循环节的计算规律是真不可背诵的 不过可以随机化,依据生日悖论寻找,\(O(\sqrt P)\) 具体来说就是猜测最小循环节上界,大致猜个 \(12P\) 然后在这个范围内随机个 \(i\),看算出的 \((F_i,F_{i+1})\) 是否与之前重复 若是,重复的为 \((F_j,F_{j+1})\),那么一定有个循环节 \(|i-j|\) 期望 \(O(\sqrt P)\) 次可以找到 下面是 \(P4000\) 斐波那契数列 交了三遍才过呢 \(\text{Code}\) $\text{Code}$ #include <bits/stdc++.h> #define IN inline using namespace std; typedef long long LL; typedef unsigned long long ull; LL P; struct Matrix { LL s00, s01, s10, s11; IN Matrix(LL a0 = 0, LL a1 = 0, LL a2 = 0, LL a3 = 0){s00 = a0, s01 = a1, s10 = a2, s11 = a3;} IN Matrix operator * (const Matrix &B) { Matrix C; C.s00 = (s00 * B.s00 % P + s01 * B.s10 % P) % P; C.s01 = (s00 * B.s01 % P + s01 * B.s11 % P) % P; C.s10 = (s10 * B.s00 % P + s11 * B.s10 % P) % P; C.s11 = (s10 * B.s01 % P + s11 * B.s11 % P) % P; return C; } }I, B; const int M = 1 << 18; struct KSM { Matrix f[M + 5], ff[M + 5]; IN void init(int m) { f[0] = ff[0] = I; for(int i = 1; i <= m; i++) f[i] = f[i - 1] * B; for(int i = 1; i <= m; i++) ff[i] = ff[i - 1] * f[m]; } IN Matrix pow(LL x){return f[x & (M - 1)] * ff[x >> 18];} }F; const int N = 3e7 + 5; char str[N]; mt19937_64 rnd(time(0)); unordered_map<ull, LL> hs; int main() { scanf("%s%d", str, &P), I = Matrix(1, 0, 0, 1), B = Matrix(0, 1, 1, 1), F.init(M); LL len = 0; while (1) { LL x = (rnd() << 28 >> 28); Matrix C = F.pow(x); ull val = ((ull)C.s00 << 32) | C.s01; if (hs.find(val) != hs.end()){len = abs(x - hs[val]); break;} hs[val] = x; } LL z = 0; for(int i = 0; i < strlen(str); i++) z = (z * 10 + (str[i] ^ 48)) % len; printf("%lld\n", F.pow(z).s01); }