UOJ NOI Round #6
总结
只打了 ,想正解无果以致于没时间打暴力
考虑到最后一个面基者要么落在点上,要么落在边上
所以可以枚举点和边,统计最久的落在此处的时间
落在边时考虑到有两个端点作为入口,需要一部分人在一端入,另一部分人从另一端入
这时贪心处理,按从某一端入需要的时间从小到大排序,前缀入这端,后缀入另一端
于是就是最短路预处理加统计,
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> #define IN inline using namespace std; typedef long long ll; const int N = 1e5 + 5; const ll INF = 1e12; int n, m, K, h[N], tot; struct Edge{int x, y, z;}E[N * 2]; struct edge{int to, nxt, w;}e[N * 4]; IN void add(int u, int v, int w){e[++tot] = edge{v, h[u], w}, h[u] = tot;} IN void read(int &x) { x = 0; char ch = getchar(); int f = 1; for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar()); for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar()); x *= f; } struct node { int x; ll d; IN bool operator < (node c) const {return d > c.d;} }; priority_queue <node> Q; int vis[N], key[N]; ll f[23][N]; IN void dijsktra(int x) { memset(vis, 0, sizeof vis); for(int i = 1; i <= n; i++) f[x][i] = INF; Q.push(node{key[x], f[x][key[x]] = 0}); while(!Q.empty()) { node now = Q.top(); Q.pop(); if (vis[now.x]) continue; vis[now.x] = 1; for(int i = h[now.x]; i; i = e[i].nxt) { int v = e[i].to; if (f[x][v] > f[x][now.x] + e[i].w) { f[x][v] = f[x][now.x] + e[i].w; Q.push(node{v, f[x][v]}); } } } } struct nod {ll x, y;}g[N]; IN bool cmp(nod a, nod b) {return a.x > b.x;} IN ll calc(int id) { ll res = INF; for(int i = 1; i <= K; i++) g[i] = nod{f[i][E[id].x], f[i][E[id].y]}; sort(g + 1, g + K + 1, cmp); ll Y = 0; for(int i = 1; i <= K; i++) { if (abs(g[i].x - Y) <= E[id].z) res = min(res, g[i].x + Y + E[id].z); Y = max(Y, g[i].y); } return res; } int main() { read(n), read(m); for(int i = 1, x, y, z; i <= m; i++) read(x), read(y), read(z), add(x, y, z), add(y, x, z), E[i] = Edge{x, y, z}; read(K), ++K, key[1] = 1; for(int i = 2; i <= K; i++) read(key[i]); for(int i = 1; i <= K; i++) dijsktra(i); ll ans = INF; for(int i = 1; i <= n; i++) { ll d = 0; for(int j = 1; j <= K; j++) d = max(d, f[j][i]); ans = min(ans, d * 2); } for(int i = 1; i <= m; i++) ans = min(ans, calc(i)); printf("%lld\n", ans); }
就不会了
有点思路:贪心考虑,分治加主席树维护
考虑一个询问,从前往后两个两个选时,假设这两个都选,超过限制,扔掉选了的最小的
同理可以从后往前两个两个选,假设这两个都不选,超过限制,选没选的最大的
那么一个询问区间 可以拼凑起来,假设分割点是
用主席树维护维护 的每个后缀选了的数, 的每个前缀没选的数
考虑拼起来时,前一段某些选了的数被后一段某些没选的数替换掉
数量相同,且前面最大的数小于后面最小的数
那么就可以二分替换数量,维护前一段的前 小和后一段的前 大
注意到分割拼凑可以分治,于是就是 的了
烂掉了, 交互题做得太少,且本身也不会高点的暴力分
看错题。。。看对了也只能拿十分
我们只考虑集合中的数选还是不选
那么就是要找到两个不相交的子集,使得它们各自和相等
也等价于找到两个可相交的子集,使得它们各自和相等
因为可以不选,把两个子集相同的数去掉即可
又看到这个限制: 其实是保证有解
子集和范围在 ,和的数量又是 的
根据鸽巢原理,必然有解,使得两个子集和相等
考虑子集和为 的数量
那么
若答案的子集和在区间 内,那么
于是可以二分确定答案的子集和
那么如何计算 呢
考虑折半搜索,分成两个集合,全集的某子集和就是两个集合各自的一个元素加起来
不难想到分别排序后双指针统计
但和是模 意义下的,若要取模,这部分范围在 ,且仍具单调性
所以四个指针维护两个区间即可
#include <cstdio> #include <algorithm> #include <iostream> #define IN inline typedef long long LL; using namespace std; const int N = 45, M = 1e6 + 1e5; int n, ans[N]; LL a[N], p; int ct1, ct2; struct node {LL v; int s;}g1[M], g2[M]; IN bool cmp(node a, node b) {return a.v < b.v;} void dfs1(int x, LL v, int s) { if (x > n / 2) return g1[++ct1] = node{v, s}, void(); dfs1(x + 1, (v + a[x]) % p, s | (1 << x - 1)), dfs1(x + 1, v, s); } void dfs2(int x, LL v, int s) { if (x > n) return g2[++ct2] = node{v, s}, void(); dfs2(x + 1, (v + a[x]) % p, s | (1 << x - 1 - n / 2)), dfs2(x + 1, v, s); } int S[7]; IN void trans(int x, int m, int f) { for(int p = 1; x; x >>= 1, p++) if (x & 1) ans[p + m] += f; } IN void update(int i, int l, int r) { for(int j = l; S[0] < 4 && j < r; j++) S[++S[0]] = g1[i].s, S[++S[0]] = g2[j].s; } IN int check(LL L, LL R) { if (L > R) return 0; LL res = 0; S[0] = 0; for(int i = 1, l1 = 1, r1 = 1, r2 = 1, l2 = 1; i <= ct1; i++) { while (l1 <= ct2 && g1[i].v + g2[l1].v < L) ++l1; while (r1 <= ct2 && g1[i].v + g2[r1].v <= R) ++r1; while (l2 <= ct2 && g1[i].v + g2[l2].v < L + p) ++l2; while (r2 <= ct2 && g1[i].v + g2[r2].v <= R + p) ++r2; res += r1 - l1 + r2 - l2, update(i, l1, r1), update(i, l2, r2); } return res > R - L + 1; } int main() { scanf("%d%lld", &n, &p); for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); dfs1(1, 0, 0), dfs2(n / 2 + 1, 0, 0), sort(g2 + 1, g2 + ct2 + 1, cmp); sort(g1 + 1, g1 + ct1 + 1, cmp), reverse(g1 + 1, g1 + ct1 + 1); LL l = 0, r = p - 1, mid = l + r >> 1; for(; l < r; mid = l + r >> 1) if (check(l, mid)) r = mid; else l = mid + 1; check(l, l), trans(S[1], 0, 1), trans(S[2], n / 2, 1), trans(S[3], 0, -1), trans(S[4], n / 2, -1); for(int i = 1; i <= n; i++) printf("%d ", ans[i]); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2020-08-08 三元上升子序列
2020-08-08 上帝造题的七分钟2 / 花神游历各国