2023.02.20【省选组】模拟 总结

summary

又是写暴力的一天

T1【USACO 2020 US Open Platinum】 Exercise

发现自己连 dp 都不会了
考场暴力都写不出来的原因:不知如何计算各个环长确定的情况下对应排列的数目
因为没有逆元,没法直接排列组合计数。。。
关键在于如果存在环长相同的情况,直接计数不可避免要除掉相同环长数量的阶乘
然而可以 dp 计数,考场想了一会却不会
考虑一个环的代表为这个环上最小的数,那么枚举当前最小值所在环的大小转移,这样就可以避免算重了

这道题又是 lcm 相关的计数
考虑单独拆开算每个质因子的指数,对某个质因子而言计算 lcm=pc 的排列数量
改成计算 pc|lcm 的排列数量,那么环中只要至少存在一个 pc 倍数的环即可
补集思想,计数不存在一个环长为 pc 倍数的排列数
若长度为 i,这个数目记为 fi,那么 fi=i!pc|jfjgij
gi 表示长度为 i 的排列环长均为 pc 的倍数的排列数目
于是就可以 O(n2) dp

warning: 加法运算整个式子的类型默认为第一个对象的类型

Code

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

T2 6517. 【GDOI2020模拟03.28】数三数(three)

仙姑了。。。

T3 6568. 【GDOI2020模拟】 迫害 DJ (hakugai)

gp 意义下是有个循环节的,且比较短,和斐波那契数列循环节一样
找到这个循环节即可,然后就是套路矩乘了

warning: 函数定义的 void 不能漏,本地编译却可通过

Code

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(P)
具体来说就是猜测最小循环节上界,大致猜个 12P
然后在这个范围内随机个 i,看算出的 (Fi,Fi+1) 是否与之前重复
若是,重复的为 (Fj,Fj+1),那么一定有个循环节 |ij|
期望 O(P) 次可以找到

下面是 P4000 斐波那契数列
交了三遍才过呢

Code

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);
}
posted @   leiyuanze  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
历史上的今天:
2020-02-20 子集选取
2020-02-20 【SDOI2015】星际战争
2020-02-20 【SDOI2015】寻宝游戏
2020-02-20 【SDOI2015】排序
2020-02-20 【雅礼联考DAY02】Revolution
2020-02-20 【雅礼联考DAY02】Magic
2020-02-20 【雅礼联考DAY01】逃跑
点击右上角即可分享
微信分享提示