//题目来源:ICPCcamp2017及其它
HDRF
Description
给出一棵 n 个节点的有根树,节点
- 从根节点出发
- 朝着当前节点子树中(不包括当前节点)最小权值点走一步
- 若当前节点是叶子,将这个节点删除,回到根节点
求删除的节点序列
n≤105
Solution
发现走到一个最小节点 u 时,在不删除完
#include<bits/stdc++.h>
using namespace std;
#define N 100001
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drp(i, a, b) for (int i = a; i >= b; i--)
#define fech(i, x) for (int i = 0; i < x.size(); i++)
#define INF 2100000000
inline int read() {
int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}
inline void write(int x) {
if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x;
char buf[20] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]);
}
int n, w[N];
vector<int> g[N];
int ind, dfq[N], getin[N], leave[N];
int mn[N << 2];
#define ls rt << 1
#define rs rt << 1 | 1
#define lson ls, l, l + r >> 1
#define rson rs, (l + r >> 1) + 1, r
inline void pushup(int rt) { mn[rt] = w[mn[ls]] < w[mn[rs]] ? mn[ls] : mn[rs]; }
void build(int rt, int l, int r) { if (!(l ^ r)) { mn[rt] = dfq[l]; return; } build(lson), build(rson), pushup(rt); }
void update(int rt, int l, int r, int pos) {
if (!(l ^ r)) { w[dfq[pos]] = INF; return; }
if (pos <= ((l + r) >> 1)) update(lson, pos); else update(rson, pos);
pushup(rt);
}
int query(int rt, int l, int r, int L, int R) {
if (l >= L && r <= R) return mn[rt];
int mid = l + r >> 1, ans1 = 0, ans2 = 0;
if (L <= mid) ans1 = query(lson, L, R);
if (R > mid) ans2 = query(rson, L, R);
return w[ans1] < w[ans2] ? ans1 : ans2;
}
#define output(u) write(u), putchar(' '), update(1, 1, n, getin[u])
void solve(int u) {
while (1) {
int v = query(1, 1, n, getin[u] + 1, leave[u]);
if (getin[u] == leave[u] || w[v] == INF) { output(u); return; }
solve(v);
}
}
void dfs(int u, int f) { dfq[getin[u] = ++ind] = u; fech(i, g[u]) if (g[u][i] ^ f) dfs(g[u][i], u); leave[u] = ind; }
inline void addEdge(int u, int v) { g[u].push_back(v); g[v].push_back(u); }
int main() {
n = read(); rep(i, 1, n) w[i] = read(); rep(i, 2, n) addEdge(read(), read());
w[0] = 0x7fffffff; dfs(1, 0); build(1, 1, n); solve(1);
return 0;
}
Shortest Path Queries
Description
给出一个
给出 Q 个询问,每次询问从一个位置到另一个位置的最短路径的长度。
Solution
线段树维护一些列里面所有点到其他列所有点的最短路。递归处理。
正在考虑实现。
子树直径
Description
小 Y 有一棵
小 J 有
这里的询问是互相独立的,即每次都是在小 Y 的原树上进行操作。
Solution
两棵树合并时,新树直径的两个端点必然来自原来两树的直径端点。
所以 dfs 序 + 线段树维护子树直径和直径端点。
#include<bits/stdc++.h>
using namespace std;
#define N 100001
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drp(i, a, b) for (int i = a; i >= b; i--)
#define fech(i, x) for (int i = 0; i < x.size(); i++)
inline int read() {
int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}
inline void write(int x) {
if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x;
char buf[20] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]);
}
int n;
struct edgeType { int u, v, w; }eg[N]; int tot;
vector<int> g[N];
int dep[N], fa[N][17], dis[N];
int dfq[N], getin[N], leave[N], ind;
struct segType { int d, a, b; bool operator < (const segType& t) const { return d < t.d; } }seg[N << 2];
#define ls rt << 1
#define rs ls | 1
#define mid (l + r >> 1)
#define lson ls, l, mid
#define rson rs, mid + 1, r
int lca(int x, int y) {
}
inline int getDis(int u, int v) { return dis[u] + dis[v] - (dis[lca(u, v)] << 1); }
inline segType choose(segType x, segType y) {
if (x.d == -1) return y; if (y.d == -1) return x;
segType res1 = segType{ getDis(x.a, y.a), x.a, y.a };
segType res2 = segType{ getDis(x.a, y.b), x.a, y.b };
segType res3 = segType{ getDis(x.b, y.a), x.b, y.a };
segType res4 = segType{ getDis(x.b, y.b), x.b, y.b };
// return max({ x, y, res1, res2, res3, res4 });
return max(x, max(y, max(res1, max(res2, max(res3, res4)))));
}
void build(int rt, int l, int r) {
if (!(l ^ r)) { seg[rt] = segType{ 0, dfq[l], dfq[l] }; return; }
build(lson), build(rson); seg[rt] = choose(seg[ls], seg[rs]);
}
void dfs(int u, int f) {
dfq[getin[u] = ++ind] = u;
fa[u][0] = f;
rep(i, 1, 17) fa[u][i] = fa[fa[u][i - 1]][i - 1];
fech(i, g[u]) {
edgeType e = eg[g[u][i]]; if (e.v == f) continue;
dis[e.v] = dis[u] + e.w; dep[e.v] = dep[u] + 1; dfs(e.v, u);
}
leave[u] = ind;
}
segType query(int rt, int l, int r, int L, int R) {
if (l >= L && r <= R) return seg[rt];
segType res = segType{ -1, 0, 0 };
if (L <= mid) res = choose(res, query(lson, L, R));
if (R > mid) res = choose(res, query(rson, L, R));
return res;
}
int x[N], s[N];
vector<int> edge[N];
int ans;
bool cmp(int a, int b) { return getin[a] < getin[b]; }
void solve(int u) {
segType res = segType{ -1, x[u], x[u] };
int L = getin[x[u]], R = leave[x[u]];
for (int i = 0; i < edge[u].size(); i++) {
int v = edge[u][i]; solve(v);
res = choose(res, query(1, 1, n, L, getin[x[v]] - 1));
L = leave[x[v]] + 1;
}
ans += choose(res, query(1, 1, n, L, R)).d;
}
int main() {
n = read(); rep(i, 2, n) {
int u = read(), v = read(), w = read();
eg[++tot] = edgeType{ u, v, w }; g[u].push_back(tot);
eg[++tot] = edgeType{ v, u, w }; g[v].push_back(tot);
}
dfs(1, 0); build(1, 1, n);
int q = read(); puts("");
printf("%d\n", lca(3, 5));
while (q--) {
int k = read();
rep(i, 1, k) {
edgeType e = eg[(read() << 1) - 1];
x[i] = dep[e.u] > dep[e.v] ? e.u : e.v;
}
sort(x + 1, x + 1 + k, cmp);
x[0] = 1; s[1] = 0; int t = 1;
rep(i, 0, k) edge[i].clear();
rep(i, 1, k) {
while (!(getin[x[s[t]]] <= getin[x[i]] && leave[x[i]] <= leave[x[s[t]]])) t--;
edge[s[t]].push_back(i);
s[++t] = i;
}
ans = 0; solve(0); write(ans); puts("");
}
return 0;
}
Common Ancestor
Description
给出两棵包含
现给出
n,q≤105
Solution
考虑在链上做的情况,显然有贡献的是一个矩形,可以用数据结构维护。在树上就同理,书上主席树。
Coprime Queries
Description
给出一个序列a1,a2,…,an,你需要回答 q 个询问
Solution
质因数分解 x,对因数做区间询问倍数,然后容斥出有多少个不和
动态背包
Description
初始时有 n 件物品,你有一个大小为
1vw :添加一个价值为 v ,大小为w 的物品- 2x:删除第 x 件物品
3 :询问用背包能装下的物品的最大价值和
n≤5000,k≤1000,q≤30000
Solution
每次加入一个物品的更新答案,删除撤销。可持久化数据结构。
奇数度数图
Description
给出一张 n 个点初始时没有边的图。按顺序依次加入
m 条边,每条边有一个边权。当加入第 i 条边时,你需要从前i 条边中选出一个子集,使得每个点的度数都为奇数。你需要让选出的边集中权值最大的边的权值尽量小,输出这个权值。Solution
奇数大小的连通块一定不合法。一个合法的图可以通过不断删除简单环并保证它仍然合法。
Matrix Recurrence
Description
Solution
双栈。当要撤销时,把右栈倒序插入左栈。前缀积。
Fold
Description
你有一个长度为 n 的纸条,接下来你会进行
m 个操作:- 1
p :把长度为 p 的左半段向右折叠 2 lr :询问在 [l,r] 这个区间中纸条总长度
n,m≤105
Solution
区间查询当然用线段树,关键是修改。发现收敛很快,所以直接暴力在线段树上更新。然而如果是这样:
复杂度就挂了。那么我们记两个指针,表示左右端点实际在哪,在出现这种情况的时候交换两个指针就可以了。
当然具体细节很麻烦……#include<bits/stdc++.h> using namespace std; #define N 100001 #define rep(i, a, b) for (int i = a; i <= b; i++) inline int read() { int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); } while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag; } inline void write(int x) { if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x; char buf[20] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]); } int n; int lft = 1, rig; int sum[N << 2]; #define ls rt << 1 #define rs ls | 1 #define mid (l + r >> 1) #define lson ls, l, mid #define rson rs, mid + 1, r #define pushup(rt) sum[rt] = sum[ls] + sum[rs] void build(int rt, int l, int r) { if (!(l ^ r)) { sum[rt] = 1; return; } build(lson), build(rson), pushup(rt); } void update(int rt, int l, int r, int pos, int val) { if (!(l ^ r)) { sum[rt] += val; return; } if (pos <= mid) update(lson, pos, val); else update(rson, pos, val); pushup(rt); } int query(int rt, int l, int r, int L, int R) { if (l >= L && r <= R) return sum[rt]; int ans = 0; if (L <= mid) ans += query(lson, L, R); if (R > mid) ans += query(rson, L, R); return ans; } int main() { n = rig = read(); build(1, 1, n); int m = read(); while (m--) { int op = read(); if (op ^ 2) { int x = read(); if (lft <= rig) if (x <= ((rig - lft + 1) >> 1)) { int p = lft + x - 1; lft = p + 1; rep(i, 1, x) update(1, 1, n, p + i, query(1, 1, n, p - i + 1, p - i + 1)); } else { int p = rig - x + 1; rig = p - 1; swap(lft, rig); rep(i, 1, x) update(1, 1, n, p - i, query(1, 1, n, p + i - 1, p + i - 1)); } else if (x <= ((lft - rig + 1) >> 1)) { int p = lft - x + 1; lft = p - 1; rep(i, 1, x) update(1, 1, n, p - i, query(1, 1, n, p + i - 1, p + i - 1)); } else { int p = rig + x - 1; rig = p + 1; swap(rig, lft); rep(i, 1, x) update(1, 1, n, p + i, query(1, 1, n, p - i + 1, p - i + 1)); } } else { int l = read(), r = read(); if (rig >= lft) l += lft - 1, r += lft - 1; else l = lft - l + 1, r = lft - r + 1; write(query(1, 1, n, l, r)); puts(""); } } return 0; }
Paper
Description
Solution
二分。主席树维护向左延伸、向右延伸。
Median on Binary Tree
Description
Solution
这个题有单调性,于是可以二分。每次加入点,在链上改。
Independent Events
Description
Solution
用泰勒展开化简一下式子,然后就是一个简单的线段树。
但是我还不会泰勒展开。
Welcome to ICPCcamp 2017
Description
Solution
一个一个加入答案,然后就用一个奇妙的 BIT 维护
Lowest Common Ancestor
Description
Solution
每个点的贡献就是在他之前的点被走了几次。然后在链上的话就是我最爱的链剖啦!再加一个差分就好了。
Longest Path
Description
Solution
点分治。然后线段树查询。
LYKMUL
Description
Solution
其实就是交乘上并。(A,B)的贡献是2y−2y−x。y是一定含
A 可以包含B的,x 是都包含的。线段树维护,删尾加头。
Subsequence Count
Description
Solution
先列出转移方程,发现可以用矩阵快速幂优化。然后发现就是交换矩阵一二行、一二列。有一个结论:乘出来以后再交换等价于先交换再乘。