@noi.ac - 441@ 你天天努力

it2022-05-08  8

目录

@description@@solution@@accepted code@@details@

@description@

你天天努力,还是比不上小牛,因为小牛在家中套路。于是你决定去拜访小牛,以请教套路的方法。

小牛住在长满多汁牧草的大草原中,草原上共有 n 个牧场,n−1 条双向道路连接这些牧场使得牧场之间两两可达。通过一条道路需要花费一定的时间,一开始这个值都是 0。

小牛并不想让你找到他,所以小牛有时候会通过一些方式使得通过某条道路的时间发生变化。于是你被小牛弄得晕头转向,不知所措。

最后你放弃了寻找小牛,开始研究起一个问题:任选两个牧场 u 和 v ,定义 dis(u,v) 为 u 到 v 的最短路,那么 dis(u,v) 最大可以是多少呢?

由于小牛操纵着牧场道路通过时间的变化,所以你需要在小牛每次操作后都重新求出这个问题的答案。

@Input format@ 第一行两个数 n,m,表示牧场的数量和小牛操作的数量。

第二行 n−1 个数,第 i 个数是 f[i+1](f[i+1]≤i),代表在 f[i+1]号牧场和 i+1 号牧场之间有一条双向道路,通过时间为 0。

接下来 m 行,每行两个数 pi,wi,表示将 f[pi] 号牧场和 pi 号牧场之间的双向道路的通过时间改为 wi。

@Output format@ 输出共 m 行,第 i 行输出一个数表示小牛第 i 次操作后 dis(u,v)的最大值。

@Sample input@ 5 4 1 1 2 2 4 8 4 3 2 2 5 7@Sample output@ 8 3 5 10@Constraints@ 对于所有的数据,n,m≤1e5,0≤wi≤2e3。

@solution@

简单来说就是在支持修改边权的操作下动态维护直径长度。

嗯。虽然我知道求直径是一个经典的 dp 问题,而这道题就是一道经典的动态 dp 问题。 不过我尝试着寻找一种比动态 dp 好写的算法(虽然写出来跟动态 dp 长度差不多。。。)。

求直径还有另一个经典的算法:点分治两次搜索。 第一次,找到距离根最远的点,不妨令其为 x。 第二次,找到距离 x 最远的点,不妨令其为 y。于是 x 与 y 的距离就是直径长。 于是我们尝试动态维护出两次搜索需要的信息。

第一次搜索,只需要支持子树的加减、查询最大值的操作即可。可以在 dfs 序上直接建一棵线段树。 第二次搜索,因为我们只需要求直径长,所以没必要维护出点 y。 因为点 x 是一个叶子结点(可能有多个结点满足离根最远,但不难发现总存在一个叶子结点满足条件),所以以点 x 为端点的直径肯定是从 x 往上爬至一定位置然后向下走。 我们再搞出两个线段树。其中一棵用于维护重链:维护重链上的点“通过轻儿子向下可到达的最远距离 - 这个点离根的距离的最大值”;另一棵维护每一个点下面挂着的轻儿子们,维护“通过这个点的轻儿子向下可到达的最远距离与次远距离”(其实这个可以不用线段树维护,只是因为我前两个需要就封装了一个,刚好就用一下。。。)。

查询时顺着重链往上时,如果遇到重链就访问我们建的第二棵线段树,遇到轻边就访问我们建的第三棵线段树,对答案进行更新。 修改时对应上面的定义恰当修改即可。不再赘述。

时间复杂度 O(nlog^2 n)

@accepted code@

#include<cstdio> #include<iostream> using namespace std; #define mp make_pair typedef pair<int, int> pii; const int MAXN = 100000; struct edge{ edge *nxt; int to; }edges[MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0]; void addedge(int u, int v) { edge *p = (++ecnt); p->to = v, p->nxt = adj[u], adj[u] = p; } struct segtree{ struct node{ int le, ri, tag; pii mx, smx; }tree[4*MAXN + 5]; bool comp(pii a, pii b) { if( a.first == 0 ) return false; if( b.first == 0 ) return true; if( a.second == b.second ) return a.first > b.first; return a.second > b.second; }// a > b : true void update(pii &mx, pii &smx, pii x) { if( comp(x, mx) ) smx = mx, mx = x; else if( comp(x, smx) ) smx = x; } void pushup(int x) { tree[x].mx = tree[x<<1|1].mx, tree[x].smx = tree[x<<1|1].smx; update(tree[x].mx, tree[x].smx, tree[x<<1].mx); update(tree[x].mx, tree[x].smx, tree[x<<1].smx); } void pushdown(int x) { if( tree[x].tag ) { tree[x<<1].mx.second += tree[x].tag, tree[x<<1].smx.second += tree[x].tag, tree[x<<1].tag += tree[x].tag; tree[x<<1|1].mx.second += tree[x].tag, tree[x<<1|1].smx.second += tree[x].tag, tree[x<<1|1].tag += tree[x].tag; tree[x].tag = 0; } } void build(int x, int l, int r) { tree[x].le = l, tree[x].ri = r, tree[x].tag = 0; if( l >= r ) { tree[x].mx = mp(l, 0), tree[x].smx = mp(0, 0); return ; } int mid = (l + r) >> 1; build(x<<1, l, mid), build(x<<1|1, mid + 1, r); pushup(x); } void add(int x, int l, int r, int d) { if( l > tree[x].ri || r < tree[x].le ) return ; if( l <= tree[x].le && tree[x].ri <= r ) { tree[x].tag += d, tree[x].mx.second += d, tree[x].smx.second += d; return ; } pushdown(x); add(x<<1, l, r, d); add(x<<1|1, l, r, d); pushup(x); } void modify(int x, int p, int k) { if( p > tree[x].ri || p < tree[x].le ) return ; if( tree[x].le == tree[x].ri ) { tree[x].mx.second = k; return ; } pushdown(x); modify(x<<1, p, k); modify(x<<1|1, p, k); pushup(x); } pii query_max(int x, int l, int r) { if( l > tree[x].ri || r < tree[x].le ) return mp(0, 0); if( l <= tree[x].le && tree[x].ri <= r ) return tree[x].mx; pushdown(x); pii a = query_max(x<<1, l, r), b = query_max(x<<1|1, l, r); return comp(a, b) ? a : b; } int query_key(int x, int p) { if( tree[x].le == tree[x].ri ) return tree[x].mx.second; int mid = (tree[x].le + tree[x].ri) >> 1; pushdown(x); if( p <= mid ) return query_key(x<<1, p); else return query_key(x<<1|1, p); } pair<pii, pii> query_sec_max(int x, int l, int r) { if( l > tree[x].ri || r < tree[x].le ) return mp(mp(0, 0), mp(0, 0)); if( l <= tree[x].le && tree[x].ri <= r ) return mp(tree[x].mx, tree[x].smx); pushdown(x); pair<pii, pii> a = query_sec_max(x<<1, l, r); pair<pii, pii> b = query_sec_max(x<<1|1, l, r); update(a.first, a.second, b.first); update(a.first, a.second, b.second); return a; } }T1, T2, T3; int f[MAXN + 5], w[MAXN + 5], n, m; int siz[MAXN + 5], hvy[MAXN + 5]; int dfn[MAXN + 5], tid[MAXN + 5], top[MAXN + 5], dcnt = 0; int num[MAXN + 5], arr[MAXN + 5], le[MAXN + 5], ri[MAXN + 5], tot = 0; void read() { scanf("%d%d", &n, &m); for(int i=2;i<=n;i++) scanf("%d", &f[i]); } void func() { for(int i=2;i<=n;i++) addedge(f[i], i); for(int i=n;i>=2;i--) siz[f[i]] += (++siz[i]); siz[1]++; for(int i=n;i>=1;i--) if( siz[hvy[f[i]]] < siz[i] ) hvy[f[i]] = i; } void dfs(int x, int tp) { dfn[++dcnt] = x, tid[x] = dcnt, top[x] = tp; if( !hvy[x] ) return ; dfs(hvy[x], tp); for(edge *p=adj[x];p;p=p->nxt) if( p->to != hvy[x] ) { arr[++tot] = p->to; if( !le[x] ) le[x] = tot; num[p->to] = ri[x] = tot; } for(edge *p=adj[x];p;p=p->nxt) if( p->to != hvy[x] ) dfs(p->to, p->to); } inline int dist(int x, int y) { return T1.query_key(1, tid[x]) - T1.query_key(1, tid[y]); } inline int find_max(int x, int y) { return T1.query_max(1, tid[y], tid[y]+siz[y]-1).second - T1.query_key(1, tid[x]); } void modify(int x, int k) { int del = k - w[x]; w[x] = k; T1.add(1, tid[x], tid[x]+siz[x]-1, del), T2.add(1, tid[x], tid[x]+siz[x]-1, -del); while( true ) { x = top[x]; if( f[x] ) { int d = T3.query_max(1, le[f[x]], ri[f[x]]).second; T3.modify(1, num[x], find_max(f[x], x)); d = T3.query_max(1, le[f[x]], ri[f[x]]).second - d; T2.add(1, tid[f[x]], tid[f[x]], d); x = f[x]; } else break; } } int query() { int x = dfn[T1.query_max(1, 1, n).first], dis = 0, ret = 0; while( true ) { int p = dfn[T2.query_max(1, tid[top[x]], tid[x] - 1).first]; ret = max(ret, dis + dist(x, p) + T3.query_max(1, le[p], ri[p]).second); dis += dist(x, top[x]), x = top[x]; if( f[x] ) { dis += w[x]; ret = max(ret, dis + find_max(f[x], hvy[f[x]])); pair<pii, pii> p = T3.query_sec_max(1, le[f[x]], ri[f[x]]); if( arr[p.first.first] == x ) ret = max(ret, dis + p.second.second); else ret = max(ret, dis + p.first.second); x = f[x]; } else break; } return ret; } void solve() { T1.build(1, 1, dcnt), T2.build(1, 1, dcnt), T3.build(1, 1, tot); for(int i=1;i<=m;i++) { int p, w; scanf("%d%d", &p, &w); modify(p, w), printf("%d\n", query()); } } int main() { read(), func(), dfs(1, 1), solve(); }

@details@

至少通过这道题我懂得了一个道理:千万不要在其他题不能确保正确性(比如对拍、手造数据、写暴力)的情况下写数据结构题。 否则你就有可能会遭遇其他题写挂,数据结构题也写挂的双挂结局。

而且写数据结构题,测完样例并没有什么用。即使没时间对拍,也要手造数据尝试 hack 自己。

转载于:https://www.cnblogs.com/Tiw-Air-OAO/p/11146855.html

相关资源:4:-源码

最新回复(0)