intcal(int x, int y){ if (y % x == 0)return0; elsereturn x - y % x; }
intmain(){ scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); if (n >= m) { printf("%d\n", n - m); continue; } int ans = INF; for (int l = 1, r; l <= n; l = r + 1) { r = min(n, m / (m / l)); ans = min(ans, n - l + cal(l, m)); ans = min(ans, n - r + cal(r, m)); } printf("%d\n", ans); } return0; }
D. Shortest Path Query
题意:给定一张带边权无向图,且保证对于每条边 (u,v,w),u 的二进制表示一定是 v 的二进制表示的前缀。多次询问,每次给定 s,t,问两点的最短路径长度。
最短路+Trie
建字典树,每点只会与祖先连边。
枚举每个点,只保留两端点都在它的子树中的边。求单源最短路。
查询时,遍历 s,t 的所有公共祖先,计算 s 到这个公共祖先的距离 + t 到这个公共祖先的距离,取最小值。
int n, m, q; ll d[N][20]; int len[N]; int ss[N], tt[N]; structE { int v; ll w; }; vector<E> G[N];
intf(int x, int p[]){ int n = 0; while (x) { p[n++] = x % 2; x >>= 1; } reverse(p, p + n); return n; }
voidbfs(int s){ priority_queue<pli, vector<pli>, greater<pli> > q; d[s][0] = 0; q.push({0, s}); while (!q.empty()) { int u = q.top().second; ll di = q.top().first; q.pop(); if (di > d[u][len[u] - len[s]])continue; for (E &e:G[u]) { int v = e.v; ll w = e.w; if (v > s && d[v][len[v] - len[s]] > di + w) { d[v][len[v] - len[s]] = di + w; q.push({di + w, v}); } } } }
intmain(){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++)len[i] = f(i, ss); for (int i = 1; i <= m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); G[u].push_back({v, w}); G[v].push_back({u, w}); } memset(d, 0x3f, sizeof(d)); for (int i = 1; i <= n; i++)bfs(i); scanf("%d", &q); while (q--) { int s, t; scanf("%d%d", &s, &t); f(s, ss); f(t, tt); int j = 0; ll ans = inf; while (ss[j] == tt[j] && j < min(len[s], len[t])) { ans = min(ans, d[s][len[s] - j - 1] + d[t][len[t] - j - 1]); j++; } if (ans == inf)puts("-1"); elseprintf("%lld\n", ans); } return0; }
B. Restore Atlantis
题意:给定 n 个矩形,q 次询问,每次s定 l,r,问编号在 [1,l)∪(r,n] 的矩形的并的面积。1≤n,q≤100000,0≤xai<xbi≤2000,0≤yai<ybi≤2000
线段树维护纵向的2000个小方块,每个节点维护两个优先队列,一个记录包含它的矩形的最小编号,另一个记录最大编号。枚举每一列,当遇到矩形左边界时,把这个矩形的编号压入矩形上下边界构成的区间中所有的小方块的优先队列中;当遇到矩形右边界时,把矩形的 vis 数组置为 1,表明这个矩形不再使用,在之后询问中必须忽略它。当边界在这一列的所有矩形都处理完后进行查询操作,从树根开始向下走,同时维护矩形编号的区间,当到达叶节点时,当前维护的这个区间就是包含这个叶节点对应的小正方形的矩形的最小最大编号。
voidupd(int ql, int qr, int id, int l, int r, int rt){ if (ql <= l && qr >= r) { trmn[rt].push(id); trmx[rt].push(id); return; } if (ql <= mid)upd(ql, qr, id, lson); if (qr > mid)upd(ql, qr, id, rson); }
vector<pii> v;
voidqry(int L, int R, int l, int r, int rt){ while (!trmn[rt].empty() && vis[trmn[rt].top()])trmn[rt].pop(); while (!trmx[rt].empty() && vis[trmx[rt].top()])trmx[rt].pop(); if (!trmn[rt].empty())L = min(L, trmn[rt].top()); if (!trmx[rt].empty())R = max(R, trmx[rt].top()); if (l == r) { if (R >= L)v.push_back({R, L}); return; } qry(L, R, lson); qry(L, R, rson); }