JZOJ 2022.07.18【提高组A】模拟
很容易想到用 来表示第 项
发现重点关注指数即可,即我们要递推 对应的指数
递推涉及 项,不难想到矩阵加速递推,
弄一个 的矩阵维护 可以表示成 及对应指数
然后就结束了
#include <cstdio> #include <cstring> #define IN inline typedef long long LL; using namespace std; const int P = 998244353, phi = P - 1; int n, K, f[205], b[205]; IN void Add(int &x, int y) {if ((x += y - phi) < 0) x += phi;} struct Matrix { int n, m, a[205][205]; IN Matrix() {n = m = 0, memset(a, 0, sizeof a);} IN Matrix operator * (const Matrix &b) { Matrix c; for(int i = 1; i <= n; i++) for(int k = 1; k <= m; k++) if (a[i][k]) for(int j = 1; j <= b.m; j++) Add(c.a[i][j], (LL)a[i][k] * b.a[k][j] % phi); c.n = n, c.m = b.m; return c; } }I, A, B, Ans; IN void Init() { I.n = I.m = K; for(int i = 1; i <= K; i++) I.a[i][i] = 1; A = I, B.n = B.m = K; for(int i = 1; i < K; i++) B.a[i][i + 1] = 1; for(int i = 1; i <= K; i++) B.a[K][i] = b[K - i + 1]; } IN Matrix M_pow(Matrix x, int y) { Matrix c = I; for(; y; x = x * x, y >>= 1) if (y & 1) c = c * x; return c; } IN int fpow(int x, int y) { int res = 1; for(; y; x = (LL)x * x % P, y >>= 1) if (y & 1) res = (LL)res * x % P; return res; } int main() { freopen("seq.in", "r", stdin); freopen("seq.out", "w", stdout); scanf("%d%d", &n, &K); for(int i = 1; i <= K; i++) scanf("%d", &b[i]); for(int i = 1; i <= K; i++) scanf("%d", &f[i]); if (n <= K) {printf("%d\n", f[n]); return 0;} Init(), Ans = A * M_pow(B, n - K); LL ans = 1; for(int i = 1; i <= K; i++) ans = ans * fpow(f[i], Ans.a[K][i]) % P; printf("%lld\n", ans); }
最先有想法的题
一看,点双!圆方树!!
然后
不过发现自己并不擅长在圆方树上的
摸索半天。。。
总结下:
这题方点用来处理环等信息,起存储作用
而 时为了方便圆点转移,并讨论情况,则需要
对于圆点,我们枚举其方点再枚举方点的儿子圆点
好处是提出了一个环,利于环上的转移
这题就是很好的案例
因为不知道这样做好转移,折腾半天。。。
#include <cstdio> #include <iostream> #include <vector> #define IN inline using namespace std; const int N = 2e4 + 5; int n, m, tot, h[N], cnt, ans; vector <int> g1[N], g2[N]; int dfn[N], low[N], stk[N], tp, dfc, bc[N], st[N], f[N], g[N]; void Tarjan(int x) { dfn[x] = low[x] = ++dfc, stk[++tp] = x; for(auto v : g1[x]) if (!dfn[v]) { Tarjan(v), low[x] = min(low[x], low[v]); if (dfn[x] == low[v]) { ++cnt, g2[x].emplace_back(cnt); for(int u = 0; u ^ v; --tp) u = stk[tp], g2[cnt].emplace_back(u); g[cnt] = g2[cnt].size() + 1; if (g[cnt] <= 2) g[x] = 0; } } else low[x] = min(low[x], dfn[v]); } void dfs(int x, int fa) { for(auto v : g2[x]) dfs(v, x), g[x] += g[v]; if (x > n && g2[x].size() == 1) g[x] = 0; if (x <= n) { for(auto v : g2[x]) { int sum = 0, t = 1; for(int i = 0; i < g2[v].size(); i++) { f[x] = max(f[x], f[g2[v][i]] + sum + t + g[x] - g[v]); sum += g[g2[v][i]], ++t; } sum = 0, t = 1; for(int i = g2[v].size() - 1; i >= 0; i--) { f[x] = max(f[x], f[g2[v][i]] + sum + t + g[x] - g[v]); sum += g[g2[v][i]], ++t; } } } f[x] = max(f[x], g[x]); } int main() { scanf("%d%d", &n, &m); for(int i = 1, x, y; i <= m; i++) scanf("%d%d", &x, &y), g1[x].emplace_back(y), g1[y].emplace_back(x); cnt = n, Tarjan(1), dfs(1, 0), printf("%d\n", f[1]); }
非常显然的莫比乌斯反演,只是需要一条结论
然后就结束了
#include <cstdio> #include <iostream> #define IN inline using namespace std; typedef long long LL; const int N = 55, M = 1e6, P = 1e9 + 9; int a[N], n, pr[M], vis[M + 5], mu[M + 5], cnt, l, r, Mn; IN void Add(LL &x, LL y) {if ((x += y - P) < 0) x += P;} void Init() { vis[1] = mu[1] = 1; for(int i = 2; i <= M; i++) { if (!vis[i]) pr[++cnt] = i, mu[i] = -1; for(int j = 1; j <= cnt && pr[j] * i <= M; j++) { vis[pr[j] * i] = 1; if (!(i % pr[j])) break; mu[pr[j] * i] = -mu[i]; } } for(int i = 2; i <= M; i++) mu[i] += mu[i - 1]; } IN int calc() { int res = 1; r = Mn; for(int i = 1; i <= n; i++) res = (LL)res * (a[i] / l) % P, r = min(r, a[i] / (a[i] / l)); return res; } int main() { freopen("point.in", "r", stdin); freopen("point.out", "w", stdout); Init(); int T; scanf("%d", &T); for(; T; --T) { scanf("%d", &n); Mn = M; for(int i = 1; i <= n; i++) scanf("%d", &a[i]), Mn = min(Mn, a[i]); LL ans = 0; int s; for(l = 1; l <= Mn; l = r + 1) s = calc(), Add(ans, (LL)(mu[r] - mu[l - 1] + P) * s % P); Mn >>= 1; for(int i = 1; i <= n; i++) a[i] >>= 1; for(l = 1; l <= Mn; l = r + 1) s = calc(), Add(ans, (LL)(mu[r] - mu[l - 1] + P) * s % P); printf("%lld\n", ans); } }
经典神题
需要发现一些性质
性质 :两个相交的优美区间相交部分一定是优美区间
用处是:考虑一个询问区间 ,如果暴力枚举
找到 且 是优美区间
那么从小到大枚举 ,第一个成功了的区间一定最短
性质 :借以判断优美区间
很容易想到区间 若满足 则此区间为优美区间
然而我不会用
另一个角度,优美区间两数差为 的数对数量是
进而可以推出区间中两数差为 的数对数量为 ,且区间长为 ,那么这个区间为优美区间
很好想
这有什么用处?
从小到大枚举 ,维护有哪些 使得 为优美区间
当 增大,即新增一个数位右端点数,我们可以快速维护
即考虑包含 的区间,这些区间两数差为 的数对数量均可加
并且只需用这个条件就可判断优美区间了,线段树支持区间加
最短的话线段树上二分找到最右的 使得 为优美区间即可
而发现一个询问可能要查询很多次而没有结果
优化是给询问排序,用堆维护当前 的询问中 从大到小的顺序
当前 下,若这个询问失败,后面的询问必然失败,可以终止查询,以少做无用功
#include <cstdio> #include <queue> #include <iostream> #include <algorithm> #define IN inline using namespace std; const int N = 1e5 + 5; int n, m, a[N], R, pos[N]; struct Que { int l, r, id; IN bool operator < (const Que &a) const {return r < a.r;} }b[N]; struct node { int l, id; IN bool operator < (const node &a) const {return l < a.l;} }; priority_queue <node> Q; struct Rev {int l, r;}ans[N]; #define ls (p << 1) #define rs (ls | 1) int mx[N << 2], tag[N << 2]; IN void pushup(int p) {mx[p] = max(mx[ls], mx[rs]);} IN void pushdown(int p) { if (!tag[p]) return; mx[ls] += tag[p], mx[rs] += tag[p], tag[ls] += tag[p], tag[rs] += tag[p]; tag[p] = 0; } void Modify(int p, int l, int r, int x, int y, int v) { if (x <= l && r <= y) return tag[p] += v, mx[p] += v, void(); pushdown(p); int mid = l + r >> 1; if (x <= mid) Modify(ls, l, mid, x, y, v); if (y > mid) Modify(rs, mid + 1, r, x, y, v); pushup(p); } int Query(int p, int l, int r, int x) { if (mx[p] < R) return 0; if (l == r) return l; pushdown(p); int mid = l + r >> 1, res = 0; if (x > mid && mx[rs] >= R) res = Query(rs, mid + 1, r, x); if (res) return res; if (mx[ls] >= R) res = Query(ls, l, mid, x); return res; } int main() { freopen("sequence.in", "r", stdin); freopen("sequence.out", "w", stdout); scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]), pos[a[i]] = i; scanf("%d", &m); for(int i = 1; i <= m; i++) scanf("%d%d", &b[i].l, &b[i].r), b[i].id = i; sort(b + 1, b + m + 1); for(int i = 1, j = 1; i <= n; i++) { R = i, Modify(1, 1, n, i, i, i); if (a[i] > 1 && pos[a[i] - 1] <= i) Modify(1, 1, n, 1, pos[a[i] - 1], 1); if (a[i] < n && pos[a[i] + 1] <= i) Modify(1, 1, n, 1, pos[a[i] + 1], 1); while (j <= m && b[j].r <= i) Q.push(node{b[j].l, b[j].id}), ++j; while (!Q.empty()) { node now = Q.top(); int gd = Query(1, 1, n, now.l); if (gd) ans[now.id] = Rev{gd, i}, Q.pop(); else break; } } for(int i = 1; i <= m; i++) printf("%d %d\n", ans[i].l, ans[i].r); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2021-07-18 LG P3768 简单的数学题
2021-07-18 NOI2018 屠龙勇士