题意就是裸的差分, 非常显然
似乎暴力存非常 GG
于是出现了以下两种解法:
解法一:线段树合并
对每个点开 vector 记录差分标记,dfs 统计答案时对每个点建一棵线段树,统计当前节点子树和
有点虚的就是这个线段树合并的复杂度问题
无脑想一想,这好像是个 O(n^2) 的
仔细考虑一下,首先分析每次合并的过程
每次合并两棵线段树子树 x 和 y
- 如果其中一棵树为空的话,返回另一棵树。
- 如果两棵树都不为空,需要递归下去合并它们的子树。
遇到第一种情况直接就返回了,最坏 logn
现在考虑第二种情况会发生多少次,
每次递归下去合并完之后我们令 x 作为 x 和 y 合并后的线段树,并返回
这样对于整个差分统计答案的过程来讲 y 这棵树就相当于删掉了,没有用了
所以这种情况的出现次数就是插入总点数级别的
接着我们考虑总点数
动态开点的话,对于每个差分的标记,最坏是要每次 logn 的
总共 m 次标记,所以总点数是 mlogn 级别的
所以合并的复杂度也是 mlogn 级别的
所以此题线段树合并统计差分答案就是 nlogn 级别的,常数略大,不过可做
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #include<vector> #define lson t[x].ch[0] #define rson t[x].ch[1] using namespace std; const int MAXN = 100005; struct EDGE{ int nxt, to; EDGE(int NXT = 0, int TO = 0) {nxt = NXT; to = TO;} }edge[MAXN << 1]; struct Node{ int ch[2], maxn, maxp; }t[MAXN * 50]; int n, m, totedge, dfnn, sizb, poolcur; int head[MAXN], siz[MAXN], fa[MAXN], dep[MAXN]; int son[MAXN], top[MAXN], dfn[MAXN], xx[MAXN]; int yy[MAXN], zz[MAXN], uni[MAXN], Root[MAXN]; int rnk[MAXN], ans[MAXN]; vector<int> tag[MAXN]; inline int rd() { register int x = 0; register char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) { x = x * 10 + (c ^ 48); c = getchar(); } return x; } inline void add(int x, int y) { edge[++totedge] = EDGE(head[x], y); head[x] = totedge; edge[++totedge] = EDGE(head[y], x); head[y] = totedge; return; } void dfs(int x) { siz[x] = 1; for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa[x]) { int y = edge[i].to; fa[y] = x; dep[y] = dep[x] + 1; dfs(y); siz[x] += siz[y]; if(siz[y] > siz[son[x]]) son[x] = y; } return; } void efs(int x) { dfn[x] = ++dfnn; if(!top[x]) top[x] = x; if(son[x]) { top[son[x]] = top[x]; efs(son[x]); } for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa[x] && edge[i].to != son[x]) efs(edge[i].to); return; } inline int lca(int x, int y) { while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x, y); x = fa[top[x]]; } return (dep[x] > dep[y] ? y : x); } inline int newnode() { return ++poolcur; } inline void pushup(int x) { if(!lson && !rson) { t[x].maxn = t[x].maxp = 0; } else if(!rson) { t[x].maxn = t[lson].maxn; t[x].maxp = t[lson].maxp; } else if(!lson) { t[x].maxn = t[rson].maxn; t[x].maxp = t[rson].maxp; } else { register int d = (t[lson].maxn < t[rson].maxn); t[x].maxn = t[t[x].ch[d]].maxn; t[x].maxp = t[t[x].ch[d]].maxp; } return; } void update(int dst, int l, int r, int &x, int val) { if(!x) x = newnode(); if(l == r) { t[x].maxn += val; t[x].maxp = (t[x].maxn ? dst : 0); return; } register int mid = ((l + r) >> 1); if(dst <= mid) update(dst, l, mid, lson, val); else update(dst, mid + 1, r, rson, val); pushup(x); return; } int Merge(int x, int y, int l, int r) { if(!x || !y) return x + y; if(l == r) { t[x].maxn += t[y].maxn; t[x].maxp = (t[x].maxn ? l : 0); return x; } int mid = ((l + r) >> 1); lson = Merge(t[x].ch[0], t[y].ch[0], l, mid); rson = Merge(t[x].ch[1], t[y].ch[1], mid + 1, r); pushup(x); return x; } void gfs(int x) { for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa[x]) { int y = edge[i].to; gfs(y); Root[x] = Merge(Root[x], Root[y], 1, sizb); } int maxi = tag[x].size(); for(int i = 0; i < maxi; ++i) { register int val = tag[x][i]; update((val < 0 ? -val : val), 1, sizb, Root[x], (val < 0 ? -1 : 1)); } ans[x] = rnk[t[Root[x]].maxp]; return; } int main() { n = rd(); m = rd(); register int xxx, yyy; for(int i = 1; i < n; ++i) { Root[i]= newnode(); xxx = rd(); yyy = rd(); add(xxx, yyy); } Root[n] = newnode(); dfs(1); efs(1); for(int i = 1; i <= m; ++i) { xx[i] = rd(); yy[i]= rd(); zz[i] = rd(); uni[i] = zz[i]; } sort(uni + 1, uni + m + 1); sizb = unique(uni + 1, uni + m + 1) - uni - 1; for(int i = 1; i <= m; ++i) { register int dst = lower_bound(uni + 1, uni + sizb + 1, zz[i]) - uni, LCA = lca(xx[i], yy[i]); rnk[dst] = zz[i]; tag[xx[i]].push_back(dst); tag[yy[i]].push_back(dst); tag[LCA].push_back(-dst); tag[fa[LCA]].push_back(-dst); } gfs(1); for(int i = 1; i <= n; ++i) printf("%d ", ans[i]); return 0; }
解法二:在树剖 dfs 序上维护差分标记
这是来自某 OJ 某题的简(shen)单(xian)解法
由于本人并无法想到如此神仙解法,就直接说做法了
在树剖 dfs 序上维护差分标记,所有标记都打在重链上
这样做的原因是重链的 dfs 序是连续的一段,不用树剖的话应该会被卡成 n^2 吧
由于是在 dfs 序上打标记,所以为了方便统计,我们把 +1 标记打在 dfs 序小的点上, -1 标记打在 dfs 序大的点上
这样打差分标记是 mlogn 的,并且常数很小
之后在统计答案时,维护一棵权值线段树,在树剖 dfs 序上扫一遍,标记该加就加该减就减,
每个节点的答案就是扫到当前节点并清理完当前节点标记之后线段树中权值最大的位置
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #include<vector> #define lson (x<< 1) #define rson ((x << 1) | 1) using namespace std; const int MAXN = 100005; struct EDGE{ int nxt, to; EDGE(int NXT = 0, int TO = 0) {nxt = NXT; to = TO;} }edge[MAXN << 1]; struct Node{ int maxn, maxp; }t[MAXN << 2]; int n, m, totedge, dfnn, sizb; int head[MAXN], fa[MAXN], siz[MAXN], dep[MAXN]; int dfn[MAXN], top[MAXN], son[MAXN], ref[MAXN]; int xx[MAXN], yy[MAXN], zz[MAXN], uni[MAXN]; int rnk[MAXN], ans[MAXN]; vector<int> tag[MAXN]; inline int rd() { register int x = 0; register char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) { x = x * 10 + (c ^ 48); c = getchar(); } return x; } inline void add(int x, int y) { edge[++totedge] = EDGE(head[x], y); head[x] = totedge; edge[++totedge] = EDGE(head[y], x); head[y] = totedge; return; } void dfs(int x) { siz[x] = 1; for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa[x]) { int y = edge[i].to; fa[y] = x; dep[y] = dep[x] + 1; dfs(y); siz[x] += siz[y]; if(siz[y] > siz[son[x]]) son[x] = y; } return; } void efs(int x) { dfn[x] = ++dfnn; ref[dfnn] = x; if(!top[x]) top[x] = x; if(son[x]) { top[son[x]] = top[x]; efs(son[x]); } for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa[x] && edge[i].to != son[x]) efs(edge[i].to); return; } inline void dltcover(int x, int y, int z) { while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x, y); tag[dfn[top[x]]].push_back(z); tag[dfn[x] + 1].push_back(-z); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x, y); tag[dfn[x]].push_back(z); tag[dfn[y] + 1].push_back(-z); return; } inline void pushup(int x) { t[x] = t[(x << 1) + (t[lson].maxn < t[rson].maxn)]; return; } void update(int dst, int l, int r, int x, int val) { if(l == r) { t[x].maxn += val; t[x].maxp = (t[x].maxn ? dst : 0); return; } register int mid = ((l + r) >> 1); if(dst <= mid) update(dst, l, mid, lson, val); else update(dst, mid + 1, r, rson, val); pushup(x); return; } inline void print(int x) { register int y = 10, len = 1; while(y <= x) {y *= 10; ++len;} while(len--) {y /= 10; putchar(x / y + 48); x %= y;} putchar(' '); return; } int main() { n = rd(); m = rd(); register int xxx, yyy; for(int i = 1; i < n; ++i) { xxx = rd(); yyy = rd(); add(xxx, yyy); } dfs(1); efs(1); for(int i = 1; i <= m; ++i) { xx[i] = rd(); yy[i] = rd(); zz[i] = rd(); uni[i] = zz[i]; } sort(uni + 1, uni + m + 1); sizb = unique(uni + 1, uni + m + 1) - uni - 1; for(int i = 1; i <= m; ++i) { register int dst = lower_bound(uni + 1, uni + sizb + 1, zz[i]) - uni; rnk[dst] = zz[i]; dltcover(xx[i], yy[i], dst); } for(int i = 1; i <= dfnn; ++i) { int maxj = tag[i].size(); for(int j = 0; j < maxj; ++j) { register int val = tag[i][j]; update((val < 0 ? -val : val), 1, sizb, 1, (val < 0 ? -1 : 1)); } ans[ref[i]] = rnk[t[1].maxp]; } for(int i = 1; i <= n; ++i) print(ans[i]); return 0; }
目前luogu最快,bzoj卡不动