JZOJ 2022.07.06【提高组A】模拟
历程
被暴打了
原因是钻进了 的坑中。。。
先看完题,发现 比较有意思, 没有想法
挺容易,做法似乎很好想
送分,十几分钟搞定
然后开 ,推了一下,明白题目需要我们干的事情
开码,发现修改与标记有些烦,然后标记打错了,一直调到 点
疯了!
还有 ,只要忍痛弃了
看看 ,很好做, 打完
有充满自信,放弃 部分分,继续调
但始终没有意识到一处标记的问题
导致比赛结束被暴打(更重要的是,暴力每题普遍 !!神仙数据。。。)
这不 预处理后分层最短路 啊!
正好复习最短路模板
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #define RE register using namespace std; const int N = 105, INF = 0x3f3f3f3f; int n, m, q, G[N][N], vis[N][N], bz[N][15], f[N][15]; struct node{int x, k;}; queue<node> Q; void SPFA() { memset(f, INF, sizeof f), Q.push(node{1, 0}), f[1][0] = 0, bz[1][0] = 1; while (!Q.empty()) { node z = Q.front(); Q.pop(); for(RE int i = 1; i <= n; i++) if (vis[z.x][i] && i ^ z.x) { int j = i, k = z.k, w = G[z.x][i]; if (!vis[i][z.x]) w *= 2, ++k; if (k <= q && f[j][k] > f[z.x][z.k] + w) { f[j][k] = f[z.x][z.k] + w; if (!bz[j][k]) Q.push(node{j, k}), bz[j][k] = 1; } } bz[z.x][z.k] = 0; } } int main() { scanf("%d%d%d", &n, &m, &q), memset(G, INF, sizeof G); for(RE int i = 1, x, y, z; i <= m; i++) scanf("%d%d%d", &x, &y, &z), G[x][y] = min(G[x][y], z), vis[x][y] = 1; for(RE int k = 1; k <= n; k++) for(RE int i = 1; i <= n; i++) if (i ^ k) for(RE int j = 1; j <= n; j++) if (j ^ i && j ^ k) vis[i][j] = (vis[i][j] | (vis[i][k] && vis[k][j])); SPFA(); int ans = INF; for(RE int i = 1; i <= q; i++) ans = min(ans, f[n][i]); if (ans == INF) ans = -1; printf("%d\n", ans); }
思考答案的产生
从 的首位, 的某个位置开始,求
然后魔法地同时跳过一格(计入贡献),再求
答案取最大值即可
也就是说可以枚举 的开始位置,二分加哈希求 即可
思维真不高。。。
也算复习字符串基操吧
#include <cstdio> #include <cstring> #include <iostream> #define RE register #define IN inline using namespace std; typedef long long LL; const int N = 5e4 + 5, P1 = 1e9 + 7, P2 = 1e9 + 9, B = 29; char s1[N], s2[N]; LL b1[N], b2[N]; struct Hash{ int n; LL p1[N], p2[N]; IN void init(char *s) { n = strlen(s + 1); for(RE int i = 1; i <= n; i++) p1[i] = (p1[i - 1] * B + s[i] - 'a') % P1, p2[i] = (p2[i - 1] * B + s[i] - 'a') % P2; } IN int get1(int l, int r){return (p1[r] - p1[l - 1] * b1[r - l + 1] % P1 + P1) % P1;} IN int get2(int l, int r){return (p2[r] - p2[l - 1] * b2[r - l + 1] % P2 + P2) % P2;} }h1, h2; IN int lcp(int l1, int l2) { int l = 0, r = min(h1.n - l1 + 1, h2.n - l2 + 1), mid = l + r >> 1, res = 0; for(; l <= r; mid = l + r >> 1) if ((h1.get1(l1, l1 + mid - 1) == h2.get1(l2, l2 + mid - 1)) && (h1.get2(l1, l1 + mid - 1) == h2.get2(l2, l2 + mid - 1))) res = mid, l = mid + 1; else r = mid - 1; return res; } int main() { b1[0] = b2[0] = 1; for(RE int i = 1; i <= N - 3; i++) b1[i] = b1[i - 1] * B % P1, b2[i] = b2[i - 1] * B % P2; scanf("%s%s", s1 + 1, s2 + 1), h1.init(s1), h2.init(s2); int ans = 0; for(RE int i = 1, len; i <= h2.n; i++) ans = max(ans, (len = lcp(1, i)) + lcp(len + 2, i + len + 1) + (len < h1.n && i + len - 1 < h2.n)); printf("%d\n", ans); }
考虑一个点的答案,显然由子树符合条件的 值转来
发现是维护子树的问题,所以树上启发式合并加树状数组做到 或者线段树合并做到
当然是打前者啦,跟打暴力一样一样的
#include <cstdio> #include <iostream> #define RE register #define IN inline using namespace std; const int N = 1e5 + 5; int n, fa[N], a[N], h[N], tot, c[N]; int dfc, siz[N], dfn[N], rev[N], son[N], ans[N]; struct edge{int to, nxt;}e[N]; IN void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;} void dfs1(int x) { siz[x] = 1, dfn[x] = ++dfc, rev[dfc] = x; for(RE int i = h[x]; i; i = e[i].nxt) { int v = e[i].to; dfs1(v), siz[x] += siz[v]; if (siz[v] > siz[son[x]]) son[x] = v; } } IN int lowbit(int x){return x & (-x);} IN void update(int x, int v){for(; x <= n; x += lowbit(x)) c[x] = max(c[x], v);} IN void clear(int x){for(; x <= n; x += lowbit(x)) c[x] = 0;} IN int query(int x) { int res = 0; for(; x; x -= lowbit(x)) res = max(res, c[x]); return res; } void dfs2(int x, int kp) { for(RE int i = h[x]; i; i = e[i].nxt) { int v = e[i].to; if (v == son[x]) continue; dfs2(v, 0); } if (son[x]) dfs2(son[x], 1); for(RE int i = h[x]; i; i = e[i].nxt) { int v = e[i].to; if (v == son[x]){update(a[son[x]], ans[son[x]]); continue;} for(RE int j = dfn[v]; j <= dfn[v] + siz[v] - 1; j++) update(a[rev[j]], ans[rev[j]]); } ans[x] = query(a[x]) + 1; if (!kp) for(RE int i = dfn[x] + 1; i <= dfn[x] + siz[x] - 1; i++) clear(a[rev[i]]); } int main() { scanf("%d", &n); int x; scanf("%d", &x); for(RE int i = 2; i <= n; i++) scanf("%d", &fa[i]), add(fa[i], i); for(RE int i = 1; i <= n; i++) scanf("%d", &a[i]); dfs1(1), dfs2(1, 1); for(RE int i = 1; i <= n; i++) printf("%d ", ans[i]); }
很容易想到 游戏的结论:异或和为零为先手必败态
那么先求异或和,需要第一步取完后留给对手必败态,即
需 最大化,可持久化 即可
但是需要支持修改操作,所以考虑分块加
注意打标记时的细节
#include <cstdio> #include <cmath> #include <iostream> #define IN inline #define RE register using namespace std; const int N = 1e5 + 5, B = 319; int n, m; struct DS{ int pos[N], block, a[N], tag[B], tr[N * 20][2], size, sum[N * 20], rt[B]; IN void init() { block = sqrt(n); for(RE int i = 1; i <= n; i++) pos[i] = (i - 1) / block + 1; } IN void update(int p, int x, int v) { if (!rt[p]) rt[p] = ++size; int u = rt[p]; for(RE int i = 10; i >= 0; i--) { int ch = (x >> i) & 1; if (!tr[u][ch]) tr[u][ch] = ++size; sum[u = tr[u][ch]] += v; } } IN int query(int p, int v) { int u = rt[p], res = 0; for(RE int i = 10; i >= 0; i--) { int ch = (v >> i) & 1; if (sum[tr[u][ch]]) u = tr[u][ch]; else if (sum[tr[u][ch ^ 1]]) u = tr[u][ch ^ 1], res |= (1 << i); } return res; } IN int get(int x){return a[x] ^ tag[pos[x]] ^ a[x + 1] ^ tag[pos[x + 1]];} IN void Modify(int r, int v) { int t = get(r), w = a[r] ^ (t - v) ^ tag[pos[r]] ^ a[r + 1] ^ tag[pos[r + 1]]; for(RE int i = r; i >= (pos[r] - 1) * block + 1; i--) update(pos[r], a[i], -1), update(pos[r], a[i] ^= w, 1); for(RE int i = 1; i < pos[r]; i++) tag[i] ^= w; } IN int Query(int l, int r, int v) { int res = N; for(RE int i = l; i <= min(pos[l] * block, r); i++) res = min(res, a[i] ^ tag[pos[l]] ^ v); if (pos[l] ^ pos[r]) for(RE int i = (pos[r] - 1) * block + 1; i <= r; i++) res = min(res, a[i] ^ tag[pos[r]] ^ v); for(RE int i = pos[l] + 1; i < pos[r]; i++) res = min(res, query(i, v ^ tag[i])); return res; } }T; int main() { scanf("%d", &n); for(RE int i = 1; i <= n; i++) scanf("%d", &T.a[i]); T.init(); for(RE int i = n; i; i--) T.a[i] ^= T.a[i + 1], T.update(T.pos[i], T.a[i], 1); scanf("%d", &m); for(RE int i = 1, ed, L, R; i <= m; i++) { scanf("%d%d%d", &ed, &L, &R); int ans = T.get(ed) - T.Query(L, R, T.a[ed] ^ T.tag[T.pos[ed]]); if (ans <= 0) ans = -1; if (ans != -1) T.Modify(ed, ans); printf("%d\n", ans); } }
可见这套题挺简单的,但是考场要分配好时间,避免陷入一处坑很久导致意外或者“正常”死亡
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2021-07-06 Luogu P3601 签到题
2021-07-06 二进制枚举子集