GDOI之后写的第一道题。看到之后没什么感觉(是我太弱,中途一度想用kpm之前在某道题上用过的链表的方法。想了想应该不可能。)
好!让我们来分析这道题吧!首先简化模型,它是要求维护树上的一些路径,支持添加和修改,要求不经过某个点的路径的最大权值(不经过某个点,我一度想到了动点分,虽然我还不会)。
我们可以先考虑在链上(其实仔细一想,如果链上的你会做,那么树上的大多数情况下便是多个了链剖而已吧!)的情况。在链上,有一些区间覆盖,要求没有覆盖某个点的区间的最大权值。那么我们接着想如果询问2询问了一个点,那么所有覆盖了这个点的区间都是无效的。换句话说如果A这个区间内含有N个点,那么A这个区间对于这N个点是无效,而对于其他的所有点都是有效的。所以接下来,如果是链上的,一个区间的有效范围便是 1—— 左端点-1, 右端点+1——N。 再把它搬回树上,接下来链剖之后,x -- y 的路径可能有 log2N个区间, 要一个个修改。再乘上线段树的log2N。 复杂度大概是log2N的平方*m。 (另外因为维护最大值所以线段树上的节点都是堆,当然C++的肯定就用优先队列了。)
1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #include<algorithm> 5 #define rep(i,j,k) for(register int i = j; i <= k; i++) 6 #define ez(i,j) for(edge*i = head[j]; i; i=i->next) 7 #define maxn 100233 8 using namespace std; 9 10 struct edge{ int to; edge*next; } e[maxn*2], *pt = e, *head[maxn]; 11 inline void add(int x,int y) { pt->to = x, pt->next = head[y], head[y] = pt++; pt->to = y, pt->next = head[x], head[x] = pt++; } 12 13 int fa[maxn], dep[maxn], top[maxn], w[maxn], son[maxn], sz[maxn], tot = 0; 14 #define to i->to 15 inline void dfs1(int x) { 16 sz[x] = 1; son[x] = 0; 17 ez(i,x) { 18 if( to == fa[x] ) continue; 19 fa[to] = x; dep[to] = dep[x] + 1; dfs1(to); 20 sz[x] += sz[to]; if( sz[to] > sz[son[x]] ) son[x] = to; 21 } 22 } 23 inline void dfs2(int x,int ph) { 24 top[x] = ph; w[x] = ++tot; 25 if( son[x] ) dfs2(son[x],ph); 26 ez(i,x) if( to != son[x] && to != fa[x] ) dfs2(to,to); 27 } 28 29 inline int read() { 30 int s = 0, t = 1; char c = getchar(); 31 while( !isdigit(c) ) { if( c == ‘-‘ ) t = -1; c = getchar(); } 32 while( isdigit(c) ) s = s * 10 + c - 48, c = getchar(); 33 return s * t; 34 } 35 struct node{ int l, r; bool operator < (const node&rhs) const { return l < rhs.l; } } nod[maxn]; 36 priority_queue<int> a[maxn*4], b[maxn*4]; 37 inline int attop(int x) { 38 while( !a[x].empty() && !b[x].empty() ) { 39 if( a[x].top() == b[x].top() ) a[x].pop(), b[x].pop(); 40 else break; 41 } 42 if( a[x].empty() ) return -1; 43 else return a[x].top(); 44 } 45 int ql, qr, n, m, d; 46 #define mid ((l+r)>>1) 47 inline void add(int l,int r,int k,int flag) { 48 if( ql > qr ) return; 49 if( ql <= l && r <= qr ) { if( flag ) a[k].push(d); else b[k].push(d); return; } 50 if( ql <= mid ) add(l,mid,k<<1,flag); if( qr > mid ) add(mid+1,r,k<<1|1,flag); 51 } 52 53 inline void update(int x,int y,int flag) { 54 int tot = 0; 55 while( top[x] != top[y] ) { 56 if( dep[top[x]] < dep[top[y]] ) swap(x,y); 57 nod[++tot].l = w[top[x]], nod[tot].r = w[x]; 58 x = fa[top[x]]; 59 } 60 if( dep[x] > dep[y] ) swap(x,y); 61 nod[++tot].l = w[x], nod[tot].r = w[y]; 62 sort(nod+1,nod+tot+1); 63 int nowl = 1; 64 rep(i,1,tot) { 65 ql = nowl, qr = nod[i].l - 1; 66 add(1,n,1,flag); 67 nowl = nod[i].r + 1; 68 } 69 ql = nowl, qr = n; 70 add(1,n,1,flag); 71 } 72 inline int query(int l,int r,int k) { 73 if( l == r ) return attop(k); 74 if( ql <= mid ) return max(attop(k),query(l,mid,k<<1)); 75 else return max(attop(k),query(mid+1,r,k<<1|1)); 76 } 77 78 int q1[maxn<<1], q2[maxn<<1], q3[maxn<<1]; 79 80 int main() { 81 n = read(), m = read(); int flag, x, y; 82 rep(i,1,n-1) add(read(),read()); 83 dfs1(1); dfs2(1,1); 84 rep(i,1,m) { 85 flag = read(); 86 if( !flag ) q1[i] = read(), q2[i] = read(), d = q3[i] = read(), update(q1[i],q2[i],1); 87 else if( flag == 1 ) x = read(), d = q3[x], update(q1[x],q2[x],0); 88 else ql = qr = w[read()], printf("%d\n", query(1,n,1)); 89 } 90 return 0; 91 }
整体二分
简单讲一下cdq分治和整体二分的区别吧!(基于我的理解)。 cdq分治通常使用在一些有多个约束条件的场合,为了去掉一些约束条件,而使用。总的来说还是用数据结构维护一些数据,只是用了这种思想后可以大大简化问题(例如把二维的题变成一维之类的),在NlongN时间内解答。而整体二分是二分答案,把问题从一个求值的题,转变成为判定性问题,通过一些途径,判断答案是否能达到某个值。因此整体二分一般应该是用来求最值(或第k大)一类的(我的理解)。
在这里,如果一条路径经过了某个点。 那么它的一个终点(或者两个)必定在以该点为根的子树内。那我们想一下如果两个终点都在以该点为根的子树内,那么该点必定是这两个终点的lca。
而如果两个终点都在以该点为根的子树,而不经过该点,那么该点必定是这两个终点的lca的祖先。因此就可以维护经过某个点的路径有多少。
二分答案K,维护权值大于答案K的路径。假设当前权值大于答案K的路径有num条,而当前经过某个点的路径恰为num条,那么对于这个询问答案必定小于K
1 #include<cstdio> 2 #include<iostream> 3 #define rep(i,j,k) for(register int i = j; i <= k; i++) 4 #define dow(i,j,k) for(register int i = j; i >= k; i--) 5 #define ez(i,j) for(edge*i = head[j]; i; i=i->next) 6 #define maxn 102333 7 using namespace std; 8 9 struct edge{ int to; edge*next; } e[maxn<<1], *pt = e, *head[maxn]; 10 inline void add(int x,int y) { pt->to = y, pt->next = head[x], head[x] = pt++; pt->to = x, pt->next = head[y], head[y] = pt++; } 11 12 inline int read() { 13 int s = 0, t = 1; char c = getchar(); 14 while( !isdigit(c) ) { if( c == ‘-‘ ) t = -1; c = getchar(); } 15 while( isdigit(c) ) s = s * 10 + c - 48, c = getchar(); 16 return s * t; 17 } 18 19 int tot = 0, n, m, dep[maxn], bin[17], fa[maxn][17], t[maxn] = {0}, ti[maxn] = {0}, now = 0, L[maxn], R[maxn]; 20 #define lower(x) (x & -x) 21 #define to i->to 22 inline int query(int x) { int ret = 0; while( x ) { if( ti[x] == now ) ret += t[x]; x -= lower(x); } return ret; } 23 inline void update(int x,int d) { while( x <= n ) if( ti[x] == now ) t[x] += d, x += lower(x); else ti[x] = now, t[x] = d, x += lower(x); } 24 inline void dfs(int x) { 25 L[x] = ++tot; 26 rep(i,1,16) if( dep[x] >= bin[i] ) fa[x][i] = fa[fa[x][i-1]][i-1]; 27 ez(i,x) if( to != fa[x][0] ) fa[to][0] = x, dep[to] = dep[x] + 1, dfs(to); 28 R[x] = tot; 29 } 30 inline int lca(int x,int y) { 31 if( dep[x] < dep[y] ) swap(x,y); 32 if( dep[x] != dep[y] ) { 33 int dis = dep[x] - dep[y]; 34 rep(i,0,16) if( dis & bin[i] ) dis -= bin[i], x = fa[x][i]; else if( !dis ) break; 35 } 36 if( x == y ) return x; 37 dow(i,16,0) if( fa[x][i] != fa[y][i] ) x = fa[x][i], y = fa[y][i]; 38 return fa[x][0]; 39 } 40 41 struct node{ int x, y, flag, d; } nod[maxn<<1]; 42 int ans[maxn<<1], t1[maxn<<1], t2[maxn<<1], bel[maxn<<1]; 43 inline void solve(int l,int r,int al,int ar) { 44 if( al == ar ) { rep(i,l,r) ans[bel[i]] = al; return; } 45 int mid = (al + ar) >> 1; 46 int l1 = 0, l2 = 0, num = 0, x, y, z, tot; now++; 47 bool nedl = 0, nedr = 0; 48 rep(i,l,r) 49 if( !nod[bel[i]].flag ) { 50 if( nod[bel[i]].d <= mid ) { t1[++l1] = bel[i]; continue; } 51 t2[++l2] = bel[i]; num++; x = nod[bel[i]].x, y = nod[bel[i]].y, z = lca(x,y); 52 update(L[x],1); update(L[y],1); update(L[z],-1); if( fa[z][0] ) update(L[fa[z][0]],-1); 53 } else if( nod[bel[i]].flag == 1 ) { 54 if( nod[bel[i]].d <= mid ) { t1[++l1] = bel[i]; continue; } 55 t2[++l2] = bel[i]; num--; x = nod[bel[i]].x, y = nod[bel[i]].y, z = lca(x,y); 56 update(L[x],-1); update(L[y],-1); update(L[z],1); if( fa[z][0] ) update(L[fa[z][0]],1); 57 } else { 58 x = nod[bel[i]].x; 59 tot = query(R[x]) - query(L[x]-1); 60 if( tot < num ) t2[++l2] = bel[i], nedr = 1; 61 else t1[++l1] = bel[i], nedl = 1; 62 } 63 rep(i,l,l+l1-1) bel[i] = t1[i-l+1]; 64 rep(i,l+l1,r) bel[i] = t2[i-l-l1+1]; 65 if( nedl ) solve(l,l+l1-1,al,mid); 66 if( nedr ) solve(l+l1,r,mid+1,ar); 67 } 68 69 int main() { 70 n = read(), m = read(); 71 bin[0] = 1; rep(i,1,16) bin[i] = bin[i-1] << 1; 72 rep(i,1,n-1) add(read(),read()); dfs(1); 73 int mx = 0, x; 74 rep(i,1,m) { 75 nod[i].flag = read(); 76 if( !nod[i].flag ) nod[i].x = read(), nod[i].y = read(), nod[i].d = read(), mx = max(nod[i].d,mx); 77 else if( nod[i].flag == 1 ) x = read(), nod[i].x = nod[x].x, nod[i].y = nod[x].y, nod[i].d = nod[x].d; 78 else nod[i].x = read(); 79 } 80 rep(i,1,m) bel[i] = i, ans[i] = -1; 81 solve(1,m,-1,mx); 82 rep(i,1,m) if( nod[i].flag == 2 ) printf("%d\n", ans[i]); 83 return 0; 84 }
RunID | User | Problem | Result | Memory | Time | Language | Code_Length | Submit_Time |
1433099 | CCTVhq | 4538 | Accepted | 19924 kb | 8952 ms | C++/Edit | 3562 B | 2016-05-04 19:52:05 |
1432903 | CCTVhq | 4538 | Accepted | 47080 kb | 6628 ms | C++/Edit | 3001 B | 2016-05-04 18:30:31 |
上面那个是整体二分的。
下面是链剖的。