2022NOIP联测7 10月11日
2022NOIP联测7 10月11日
找
题解做法:
欧几里得距离公式:
直接预处理 查询。
我的做法:
将 轴与 轴分开处理,两个互不影响,最后答案加起来。
先从小到大排序,处理 的解, 转移下一个的答案,也是拆的平方差的式子,但有些麻烦。
Code moo~~
// 好像可以 O(1) 转移 #include <bits/stdc++.h> using namespace std; #define ll long long #define re register int #define pc_ putchar(' ') #define pc_n putchar('\n') #define Bessie int const int MOD = 998244353, CTR = 2e5 + 7; int n; ll ansx[CTR], ansy[CTR], sumx[CTR], sumy[CTR], sumpre, sumend; struct star { ll x, y; int idx; }a[CTR]; bool cmp1(star A, star B) { return A.x < B.x; } bool cmp2(star A, star B) { return A.y < B.y; } Bessie main() { n = read(); for(re i = 1; i <= n; ++i) { a[i].x = read(), a[i].y = read(), a[i].idx = i; } sort(a + 1, a + n + 1, cmp1); for(re i = 1; i < n; ++i) { sumx[i] = a[i + 1].x - a[i].x; sumx[i] += sumx[i - 1]; sumend = (sumend + sumx[i]) % MOD; } for(re i = 1; i < n; ++i) { ansx[a[1].idx] = (ansx[a[1].idx] + sumx[i] * sumx[i] % MOD) % MOD; } sumpre = 0; // printf("pre:%lld end:%lld\n", sumpre, sumend); for(re i = 2; i <= n; ++i) { sumend = (sumend - (sumx[i - 1] - sumx[i - 2]) * (n - i + 1) % MOD) % MOD; ansx[a[i].idx] = ( ansx[a[i - 1].idx] + (i - 2 - (n - i)) * (sumx[i - 1] - sumx[i - 2]) % MOD * (sumx[i - 1] - sumx[i - 2]) % MOD + 2 * (sumx[i - 1] - sumx[i - 2]) % MOD * sumpre % MOD - 2 * (sumx[i - 1] - sumx[i - 2]) % MOD * sumend % MOD ); sumpre = (sumpre + (sumx[i - 1] - sumx[i - 2]) * (i - 1) % MOD) % MOD; ansx[a[i].idx] = (ansx[a[i].idx] % MOD + MOD) % MOD; // printf("pre:%lld end:%lld\n", sumpre, sumend); } sumend = 0; sort(a + 1, a + n + 1, cmp2); for(re i = 1; i < n; ++i) { sumy[i] = a[i + 1].y - a[i].y; sumy[i] += sumy[i - 1]; sumend = (sumend + sumy[i]) % MOD; } for(re i = 1; i < n; ++i) { ansy[a[1].idx] = (ansy[a[1].idx] + sumy[i] * sumy[i] % MOD) % MOD; } sumpre = 0; // printf("pre:%lld end:%lld\n", sumpre, sumend); for(re i = 2; i <= n; ++i) { sumend = (sumend - (sumy[i - 1] - sumy[i - 2]) * (n - i + 1) % MOD) % MOD; ansy[a[i].idx] = ( ansy[a[i - 1].idx] + (i - 2 - (n - i)) * (sumy[i - 1] - sumy[i - 2]) % MOD * (sumy[i - 1] - sumy[i - 2]) % MOD + 2 * (sumy[i - 1] - sumy[i - 2]) % MOD * sumpre % MOD - 2 * (sumy[i - 1] - sumy[i - 2]) % MOD * sumend % MOD ); sumpre = (sumpre + (sumy[i - 1] - sumy[i - 2]) * (i - 1) % MOD) % MOD; ansy[a[i].idx] = (ansy[a[i].idx] % MOD + MOD) % MOD; // printf("pre:%lld end:%lld\n", sumpre, sumend); } for(re i = 1; i <= n; ++i) { printf("%lld\n", (ansx[i] + ansy[i]) % MOD); } return 0; }
女
题解(树剖)
考虑树上两点间的距离为 ,想到对于一个询问 来说,要找到最小的 , 是标记的黑点。
先树剖一下建线段树,每次修改时向上更新节点维护的 ,询问时也向上取最小值,直到根节点。
为什么是对的呢?
考虑所有修改与询问都向上跳时,他们最早一定在 处相遇,由上面的距离式子可知此时的答案一定是最小的,即使跳过了,由于我们一直取 ,也不会影响答案。
现在重点说一下线段树怎么维护。
维护两个值: 和 ,一个标记(子树中所有黑点的深度) 。标记永久化。
建树:
。
修改:(设修改点为 )
1.白点变黑:
中加入 , 。
2.黑点变白:
在 中删 ,将 重设为左右儿子 的较小值(如果是叶子就设为 ),再看一下子树中有没有黑点, 再更新一下取较小值。
时不仅需要左右儿子的 ,还要考虑他们的标记和当前节点的标记。
询问:
返回 ,因为标记永久化,每一次向上传时也要根据当前节点的标记再次计算答案。
每次在 multiset 中询问时先要看看是否为空!
无了。
对了,树剖别像我似的打错,指建树时不用 ,要用 。
Code moo~~
#include <bits/stdc++.h> using namespace std; #define ll long long #define re register int #define pc_ putchar(' ') #define pc_n putchar('\n') #define Bessie int const int CTR = 1e5 + 7, INF = 0x7f7f7f7f; int n, Q; bool black[CTR]; int blacknum; struct edge { int to, nxt; }e[CTR << 1]; int h[CTR], etot; void addedge(int x, int y) { e[++etot].to = y; e[etot].nxt = h[x]; h[x] = etot; } int siz[CTR], dep[CTR], fa[CTR], son[CTR]; void dfs1(int x, int f) { fa[x] = f; dep[x] = dep[f] + 1; siz[x] = 1; for(re i = h[x], v; i; i = e[i].nxt) { v = e[i].to; if(v == f) continue; dfs1(v, x); siz[x] += siz[v]; if(siz[v] > siz[son[x]]) son[x] = v; } } int top[CTR], dfn[CTR], rk[CTR], tme; void dfs2(int x, int topf) { top[x] = topf; dfn[x] = ++tme; rk[tme] = x; if(son[x]) dfs2(son[x], topf); for(re i = h[x], v; i; i = e[i].nxt) { v = e[i].to; if(v == fa[x] || v == son[x]) continue; dfs2(v, v); } } typedef pair<int, int> pii; struct Tree { int minn[CTR << 2], sum[CTR << 2]; multiset<int> S[CTR << 2]; #define ls(p) (p << 1) #define rs(p) (p << 1 | 1) #define mid ((l + r) >> 1) void built(int p, int l, int r) { sum[p] = INF; if(l == r) { minn[p] = -2 * dep[rk[l]]; return ; } built(ls(p), l, mid), built(rs(p), mid + 1, r); minn[p] = min(minn[ls(p)], minn[rs(p)]); } void update(int p, int l, int r, int L, int R, int k) { if(L <= l && r <= R) { if(black[k]) { sum[p] = min(sum[p], minn[p] + dep[k]); S[p].insert(dep[k]); } else { S[p].erase(S[p].find(dep[k])); if(l != r) { sum[p] = min(sum[ls(p)], sum[rs(p)]); } else { sum[p] = INF; } if(!S[p].empty()) { sum[p] = min(sum[p], minn[p] + *S[p].begin()); } } return; } if(L <= mid) update(ls(p), l, mid, L, R, k); if(R > mid) update(rs(p), mid + 1, r, L, R, k); int res = INF; if(!S[p].empty()) res = minn[p] + *S[p].begin(); sum[p] = min({sum[ls(p)], sum[rs(p)], res}); } pii query(int p, int l, int r, int L, int R) { if(L <= l && r <= R) { return make_pair(sum[p], minn[p]); } pii A = { INF, INF }, B = { INF, INF }; int ans1 = INF, ans2 = INF, ans3 = INF, ans4 = INF; if(L <= mid) A = query(ls(p), l, mid, L, R); if(R > mid) B = query(rs(p), mid + 1, r, L, R); ans1 = A.first, ans2 = B.first; if(!S[p].empty()) ans3 = A.second + *S[p].begin(), ans4 = B.second + *S[p].begin(); return make_pair(min({ans1, ans2, ans3, ans4}), min(A.second, B.second)); } }T; void modify(int x, int y, int k) { if(dep[x] < dep[y]) swap(x, y); while(top[x] != top[y]) { T.update(1, 1, n, dfn[top[x]], dfn[x], k); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x, y); T.update(1, 1, n, dfn[x], dfn[y], k); } int qmin(int x, int y) { int ans = INF; if(dep[x] < dep[y]) swap(x, y); while(top[x] != top[y]) { ans = min(ans, T.query(1, 1, n, dfn[top[x]], dfn[x]).first); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x, y); ans = min(ans, T.query(1, 1, n, dfn[x], dfn[y]).first); return ans; } Bessie main() { n = read(), Q = read(); for(re i = 1, x, y; i < n; ++i) { x = read(), y = read(); addedge(x, y); addedge(y, x); } dfs1(1, 1); dfs2(1, 1); T.built(1, 1, n); int op, x; while(Q--) { op = read(), x = read(); if(op == 1) { black[x] ^= 1; modify(1, x, x); if(black[x]) ++blacknum; else --blacknum; } else { if(!blacknum) ot(-1),pc_n; else ot(dep[x] + qmin(1, x)),pc_n; } } return 0; }
朋
科技题。还不会,写题目名称仅供一乐。
友
题解(树形背包+剪枝)
理论复杂度 数据水给过了。
设 为以 为根的子树选了 的容量为 时的 的最大值。
转移平凡。
一个剪枝就是要保证 (容量)合法,不合法直接 ;
上下界剪枝就是不超过子树中 之和。
Code moo~~
#include <bits/stdc++.h> using namespace std; #define ll long long #define re register int #define pc_ putchar(' ') #define pc_n putchar('\n') #define Bessie int const int CTR = 1e3 + 7; int n, m; int a[CTR], b[CTR]; int atot[CTR]; struct edge { int to, nxt; }e[CTR * CTR]; int h[CTR], etot; void addedge(int x, int y) { e[++etot].to = y; e[etot].nxt = h[x]; h[x] = etot; } int ans; int dp[CTR][CTR * 10]; void dfs1(int x, int f) { dp[x][a[x]] = max(dp[x][a[x]], b[x]); atot[x] += a[x]; for(re i = h[x], v; i; i = e[i].nxt) { v = e[i].to; if(v == f) continue; dfs1(v, x); for(re j = min(atot[x], m); j >= 0; --j) { if(dp[x][j] == -1) continue; for(re k = min(atot[v], m - j); k >= 0; --k) { if(dp[v][k] == -1) continue; dp[x][j + k] = max(dp[v][k] + dp[x][j], dp[x][j + k]); ans = max(ans, dp[x][j + k]); } } atot[x] += atot[v]; } } Bessie main() { memset(dp, -1, sizeof(dp)); n = read(), m = read(); for(re i = 1; i <= n; ++i) { a[i] = read(), b[i] = read(); } for(re i = 1, x, y; i < n; ++i) { x = read(), y = read(); addedge(x, y); addedge(y, x); } dfs1(1, 1); ot(ans); return 0; }
总结:T1 想的太慢,思路不好打,T2 用的 STL::queue ,每次清空是 的没有手写快,T3 暴力没来得及打,T4 还可以。
今个三道题一个正解都没打。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】