speed pack 1.
收录数学题
1. qoj7759/集训队互测2023 - Permutation Counting 2
tag:絶滅反演;link。
一个观察:考虑依次加入逆排列的每个连续段,相当于从小到大往原排列中填数(比如逆排列
容易优化至
但是每两个连续段可能可以合并,所以设
二维絶滅反演得:
容易优化至
点击查看代码
//qoj7759 #include <bits/stdc++.h> using namespace std; const int N = 510; typedef long long ll; int n, m; ll P, f[N][N], g[N][N], h[N][N]; ll C[N][N], fac[N*N+N], inv[N*N+N]; ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } int fy(int x){ return (x&1) ? P-1 : 1; } ll Co(int x, int y){ return fac[x] * inv[y] % P * inv[x-y] % P; } int main(){ cin >> n >> P; fac[0] = 1; m = n * n + n; for(int i = 1; i <= m; ++ i){ fac[i] = fac[i-1] * i % P; } inv[m] = qp(fac[m], P-2); for(int i = m-1; i >= 0; -- i){ inv[i] = inv[i+1] * (i+1) % P; } C[0][0] = 1; for(int i = 1; i <= n; ++ i){ C[i][0] = C[i][i] = 1; for(int j = 1; j < i; ++ j){ C[i][j] = (C[i-1][j] + C[i-1][j-1]) % P; } } for(int x = 1; x <= n; ++ x){ for(int y = 1; y <= n; ++ y){ for(int i = 1; i <= y; ++ i){ h[x][y] = (h[x][y] + C[y][i] * fy(y-i) % P * Co(x*i+n-1, x*i-1)) % P; } for(int i = 1; i <= x; ++ i){ g[x][y] = (g[x][y] + C[x][i] * fy(x-i) % P * h[i][y]) % P; } } } memset(h, 0, sizeof(h)); for(int x = 0; x < n; ++ x){ for(int y = 0; y < n; ++ y){ for(int i = y; i <= n; ++ i){ h[x][y] = (h[x][y] + C[i][y] * fy(i-y) % P * g[n-x][n-i]) % P; } } } for(int x = 0; x < n; ++ x){ for(int y = 0; y < n; ++ y){ for(int i = x; i <= n; ++ i){ f[x][y] = (f[x][y] + C[i][x] * fy(i-x) % P * h[i][y]) % P; } printf("%lld ", f[x][y]); } puts(""); } return 0; }
2. CF1973F - Maximum GCD Sum Queries
tag:高维差分;link。
不妨
设
那么每对
以及下标的因数 。 以及下标的因数 。 以及下标的因数 ,因为这部分的可行计数不能加两次。 以及下标的因数 ,表示一次交换。 以及下标的因数 ,这部分不需要交换就可以。
发现这类似于高维差分,只不过每一维对应一个质因数。所以维护过程是:
- 求解过程中,只更新上述的
个下标; - 最后还原答案,枚举
的每个质因数 以及下标 ,若 则令 加上 - 枚举
的每个质因数,同理求解。
最后将所有
复杂度瓶颈是
点击查看代码
//CF1973F #include <bits/stdc++.h> using namespace std; const int N = 5e5 + 10, M = 780; int n, q, a[N], b[N], c[N], ans[N]; typedef long long ll; ll d[N]; int cnt[M][M]; ll f[M][M]; vector<int> pa, pb, pra, prb; unordered_map<int, int> ma, mb; void solve(){ memset(f, 0, sizeof(f)); memset(cnt, 0, sizeof(cnt)); pra.clear(); prb.clear(); pa.clear(); pb.clear(); ma.clear(); mb.clear(); for(int i = 1; i * i <= a[1]; ++ i){ if(a[1] % i == 0){ pa.push_back(i); if(i * i != a[1]){ pa.push_back(a[1] / i); } } } int k = a[1]; for(int i = 2; i * i <= k; ++ i){ if(k % i == 0){ pra.push_back(i); while(k % i == 0){ k /= i; } } } if(k > 1){ pra.push_back(k); } for(int i = 1; i * i <= b[1]; ++ i){ if(b[1] % i == 0){ pb.push_back(i); if(i * i != b[1]){ pb.push_back(b[1] / i); } } } k = b[1]; for(int i = 2; i * i <= k; ++ i){ if(k % i == 0){ prb.push_back(i); while(k % i == 0){ k /= i; } } } if(k > 1){ prb.push_back(k); } sort(pa.begin(), pa.end()); sort(pb.begin(), pb.end()); for(int i = 0, k = pa.size(); i < k; ++ i){ ma[pa[i]] = i + 1; } for(int i = 0, k = pb.size(); i < k; ++ i){ mb[pb[i]] = i + 1; } for(int i = 2; i <= n; ++ i){ int p = __gcd(a[i], b[i]); ++ cnt[ma[__gcd(a[i], a[1])]][mb[__gcd(b[i], b[1])]]; ++ cnt[ma[__gcd(b[i], a[1])]][mb[__gcd(a[i], b[1])]]; -- cnt[ma[__gcd(p , a[1])]][mb[__gcd(p , b[1])]]; f[ma[__gcd(b[i], a[1])]][mb[__gcd(a[i], b[1])]] += c[i]; f[ma[__gcd(p , a[1])]][mb[__gcd(p , b[1])]] -= c[i]; } for(int i : pra){ for(int x = pa.size(); x >= 1; -- x){ for(int y = pb.size(); y >= 1; -- y){ int val = pa[x-1]; if(val % i == 0){ int p = ma[val / i]; cnt[p][y] += cnt[x][y]; f[p][y] += f[x][y]; } } } } for(int i : prb){ for(int x = pa.size(); x >= 1; -- x){ for(int y = pb.size(); y >= 1; -- y){ int val = pb[y-1]; if(val % i == 0){ int p = mb[val / i]; cnt[x][p] += cnt[x][y]; f[x][p] += f[x][y]; } } } } vector<pair<ll, int> > v; for(int x = pa.size(); x >= 1; -- x){ for(int y = pb.size(); y >= 1; -- y){ if(cnt[x][y] == n-1){ v.emplace_back(f[x][y], - pa[x-1] - pb[y-1]); } } } for(int i = 1; i <= q; ++ i){ v.emplace_back(d[i], i); } sort(v.begin(), v.end()); int mx = 0; for(auto i : v){ if(i.second > 0){ ans[i.second] = max(ans[i.second], mx); } else { mx = max(mx, -i.second); } } } int main(){ scanf("%d%d", &n, &q); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); } for(int i = 1; i <= n; ++ i){ scanf("%d", &b[i]); } for(int i = 1; i <= n; ++ i){ scanf("%d", &c[i]); } for(int i = 1; i <= q; ++ i){ scanf("%lld", &d[i]); } solve(); for(int i = 1; i <= q; ++ i){ d[i] -= c[1]; } swap(a[1], b[1]); solve(); for(int i = 1; i <= q; ++ i){ printf("%d ", ans[i]); } return 0; }
3. ARC114E - Paper Cutting 2
tag:期望;link。被剪开了/ll
把选择直线的操作写成一个排列,那么对于任意一个排列,会进行其中的若干操作(不一定是前缀)。由于期望的线性性,考虑计算每一条直线在多少排列中会被操作。
容易发现,一条直线
于是统计所有直线的贡献,并且最后
点击查看代码
//AT_arc114_e #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll P = 998244353; const int N = 2e5 + 10; ll inv[N], w, h, x, y, xx, yy, ans; int main(){ cin >> h >> w >> x >> y >> xx >> yy; if(x > xx){ swap(x, xx); } if(y > yy){ swap(y, yy); } inv[1] = 1; for(int i = 2; i < N; ++ i){ inv[i] = (P - P / i) * inv[P%i] % P; } for(int i = 1; i < h; ++ i){ if(i >= x && i < xx){ continue; } else if(i < x){ ll a = (yy - y) + (xx - i); ans = (ans + inv[a]) % P; } else if(i >= xx){ ll a = (yy - y) + (i - x + 1); ans = (ans + inv[a]) % P; } } for(int i = 1; i < w; ++ i){ if(i >= y && i < yy){ continue; } else if(i < y){ ll a = (xx - x) + (yy - i); ans = (ans + inv[a]) % P; } else if(i >= yy){ ll a = (xx - x) + (i - y + 1); ans = (ans + inv[a]) % P; } } printf("%lld\n", (ans + 1) % P); return 0; }
类似的题目:CF1924E - Paper Cutting Again。
发现对于一条直线,不能在它之后选的直线依旧是一个集合,于是一样做即可。
4. LuoguP8967 - 追寻 | Pursuit of Dream
tag:期望;link。
设
其中
接着考虑散入天际的情况,设答案为
其中
发现
考虑
- 不考虑到达终点,期望步数为
; - 考虑到达终点,要减去走到终点再失足的情况
。
然后将
点击查看代码
//P8967 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 110, K = 1e4 + 10, M = 1e7 + 10; const ll P = 998244353, iv = 205817851; int n, k; ll d[N], a[K][N], p[K], pp; ll fac[M], q[K], g; ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } int main(){ fac[0] = 1; for(int i = 1; i < M; ++ i){ fac[i] = fac[i-1] * i % P; } scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++ i){ scanf("%lld", &d[i]); } for(int i = 1; i <= k; ++ i){ for(int j = 1; j <= n; ++ j){ scanf("%lld", &a[i][j]); } scanf("%lld", &p[i]); p[i] = p[i] * iv % P; pp = (pp + p[i]) % P; } for(int i = 0; i <= k; ++ i){ bool flg = 1; ll sum = 0, mul = 1; for(int j = 1; j <= n; ++ j){ if(d[j] < a[i][j]){ flg = 0; break; } sum += d[j] - a[i][j]; mul = mul * fac[d[j]-a[i][j]] % P; } if(flg){ mul = mul * qp(n, sum) % P; q[i] = qp(mul, P-2) * fac[sum] % P * qp(P + 1 - pp, sum) % P; } } ll fz = 1, fm = 0, ip = qp(pp, P-2); for(int i = 1; i <= k; ++ i){ ll tmp = p[i] * (1 - q[i] + P) % P * ip % P; fm = (fm + tmp * ip) % P; fz = (fz + P - tmp) % P; } g = fm * qp(fz, P-2) % P; ll ans = (1 + P - q[0]) * (g + ip) % P; printf("%lld\n", ans); return 0; }
5. ARC122E - Increasing LCMs
考虑每次找到一个合法的
一个
考虑如何判断
- 转化为
; - 将
与 换顺序(多个数的 不方便求); - 得到
,可以直接求解(左侧大于的时候直接判不合法)。
每次暴力找到一个
点击查看代码
// Problem: E - Increasing LCMs // Contest: AtCoder - Tokio Marine & Nichido Fire Insurance Programming Contest 2021(AtCoder Regular Contest 122) // URL: https://atcoder.jp/contests/arc122/tasks/arc122_e // Memory Limit: 1024 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 110; int n; ll a[N], b[N]; bool chk(int pos, int len){ __int128 nw = 0; for(int i = 1; i <= len; ++ i){ if(i == pos){ continue; } __int128 g = __gcd(a[i], a[pos]); if(nw == 0){ nw = g; } else { nw = nw / __gcd(nw, g) * g; } if(nw >= a[pos]){ return 0; } } return 1; } int main(){ int T = 1; while(T--){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%lld", &a[i]); } for(int i = n; i >= 1; -- i){ bool flg = 0; for(int j = 1; j <= i; ++ j){ if(chk(j, i)){ b[i] = a[j]; for(int k = j; k < i; ++ k){ a[k] = a[k+1]; } flg = 1; break; } } if(!flg){ puts("No"); return 0; } } puts("Yes"); for(int i = 1; i <= n; ++ i){ printf("%lld ", b[i]); } puts(""); } return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步