4538: [Hnoi2016]网络 链剖 + 堆(优先队列) / 整体二分
2016-05-04 20:04
302 查看
GDOI之后写的第一道题。看到之后没什么感觉(是我太弱,中途一度想用kpm之前在某道题上用过的链表的方法。想了想应该不可能。)
好!让我们来分析这道题吧!首先简化模型,它是要求维护树上的一些路径,支持添加和修改,要求不经过某个点的路径的最大权值(不经过某个点,我一度想到了动点分,虽然我还不会)。
我们可以先考虑在链上(其实仔细一想,如果链上的你会做,那么树上的大多数情况下便是多个了链剖而已吧!)的情况。在链上,有一些区间覆盖,要求没有覆盖某个点的区间的最大权值。那么我们接着想如果询问2询问了一个点,那么所有覆盖了这个点的区间都是无效的。换句话说如果A这个区间内含有N个点,那么A这个区间对于这N个点是无效,而对于其他的所有点都是有效的。所以接下来,如果是链上的,一个区间的有效范围便是 1—— 左端点-1, 右端点+1——N。 再把它搬回树上,接下来链剖之后,x -- y 的路径可能有 log2N个区间, 要一个个修改。再乘上线段树的log2N。 复杂度大概是log2N的平方*m。 (另外因为维护最大值所以线段树上的节点都是堆,当然C++的肯定就用优先队列了。)
整体二分
简单讲一下cdq分治和整体二分的区别吧!(基于我的理解)。 cdq分治通常使用在一些有多个约束条件的场合,为了去掉一些约束条件,而使用。总的来说还是用数据结构维护一些数据,只是用了这种思想后可以大大简化问题(例如把二维的题变成一维之类的),在NlongN时间内解答。而整体二分是二分答案,把问题从一个求值的题,转变成为判定性问题,通过一些途径,判断答案是否能达到某个值。因此整体二分一般应该是用来求最值(或第k大)一类的(我的理解)。
在这里,如果一条路径经过了某个点。 那么它的一个终点(或者两个)必定在以该点为根的子树内。那我们想一下如果两个终点都在以该点为根的子树内,那么该点必定是这两个终点的lca。
而如果两个终点都在以该点为根的子树,而不经过该点,那么该点必定是这两个终点的lca的祖先。因此就可以维护经过某个点的路径有多少。
二分答案K,维护权值大于答案K的路径。假设当前权值大于答案K的路径有num条,而当前经过某个点的路径恰为num条,那么对于这个询问答案必定小于K
上面那个是整体二分的。
下面是链剖的。
好!让我们来分析这道题吧!首先简化模型,它是要求维护树上的一些路径,支持添加和修改,要求不经过某个点的路径的最大权值(不经过某个点,我一度想到了动点分,虽然我还不会)。
我们可以先考虑在链上(其实仔细一想,如果链上的你会做,那么树上的大多数情况下便是多个了链剖而已吧!)的情况。在链上,有一些区间覆盖,要求没有覆盖某个点的区间的最大权值。那么我们接着想如果询问2询问了一个点,那么所有覆盖了这个点的区间都是无效的。换句话说如果A这个区间内含有N个点,那么A这个区间对于这N个点是无效,而对于其他的所有点都是有效的。所以接下来,如果是链上的,一个区间的有效范围便是 1—— 左端点-1, 右端点+1——N。 再把它搬回树上,接下来链剖之后,x -- y 的路径可能有 log2N个区间, 要一个个修改。再乘上线段树的log2N。 复杂度大概是log2N的平方*m。 (另外因为维护最大值所以线段树上的节点都是堆,当然C++的肯定就用优先队列了。)
#include<cstdio> #include<iostream> #include<queue> #include<algorithm> #define rep(i,j,k) for(register int i = j; i <= k; i++) #define ez(i,j) for(edge*i = head[j]; i; i=i->next) #define maxn 100233 using namespace std; struct edge{ int to; edge*next; } e[maxn*2], *pt = e, *head[maxn]; 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++; } int fa[maxn], dep[maxn], top[maxn], w[maxn], son[maxn], sz[maxn], tot = 0; #define to i->to inline void dfs1(int x) { sz[x] = 1; son[x] = 0; ez(i,x) { if( to == fa[x] ) continue; fa[to] = x; dep[to] = dep[x] + 1; dfs1(to); sz[x] += sz[to]; if( sz[to] > sz[son[x]] ) son[x] = to; } } inline void dfs2(int x,int ph) { top[x] = ph; w[x] = ++tot; if( son[x] ) dfs2(son[x],ph); ez(i,x) if( to != son[x] && to != fa[x] ) dfs2(to,to); } inline int read() { int s = 0, t = 1; char c = getchar(); while( !isdigit(c) ) { if( c == '-' ) t = -1; c = getchar(); } while( isdigit(c) ) s = s * 10 + c - 48, c = getchar(); return s * t; } struct node{ int l, r; bool operator < (const node&rhs) const { return l < rhs.l; } } nod[maxn]; priority_queue<int> a[maxn*4], b[maxn*4]; inline int attop(int x) { while( !a[x].empty() && !b[x].empty() ) { if( a[x].top() == b[x].top() ) a[x].pop(), b[x].pop(); else break; } if( a[x].empty() ) return -1; else return a[x].top(); } int ql, qr, n, m, d; #define mid ((l+r)>>1) inline void add(int l,int r,int k,int flag) { if( ql > qr ) return; if( ql <= l && r <= qr ) { if( flag ) a[k].push(d); else b[k].push(d); return; } if( ql <= mid ) add(l,mid,k<<1,flag); if( qr > mid ) add(mid+1,r,k<<1|1,flag); } inline void update(int x,int y,int flag) { int tot = 0; while( top[x] != top[y] ) { if( dep[top[x]] < dep[top[y]] ) swap(x,y); nod[++tot].l = w[top[x]], nod[tot].r = w[x]; x = fa[top[x]]; } if( dep[x] > dep[y] ) swap(x,y); nod[++tot].l = w[x], nod[tot].r = w[y]; sort(nod+1,nod+tot+1); int nowl = 1; rep(i,1,tot) { ql = nowl, qr = nod[i].l - 1; add(1,n,1,flag); nowl = nod[i].r + 1; } ql = nowl, qr = n; add(1,n,1,flag); } inline int query(int l,int r,int k) { if( l == r ) return attop(k); if( ql <= mid ) return max(attop(k),query(l,mid,k<<1)); else return max(attop(k),query(mid+1,r,k<<1|1)); } int q1[maxn<<1], q2[maxn<<1], q3[maxn<<1]; int main() { n = read(), m = read(); int flag, x, y; rep(i,1,n-1) add(read(),read()); dfs1(1); dfs2(1,1); rep(i,1,m) { flag = read(); if( !flag ) q1[i] = read(), q2[i] = read(), d = q3[i] = read(), update(q1[i],q2[i],1); else if( flag == 1 ) x = read(), d = q3[x], update(q1[x],q2[x],0); else ql = qr = w[read()], printf("%d\n", query(1,n,1)); } return 0; }
整体二分
简单讲一下cdq分治和整体二分的区别吧!(基于我的理解)。 cdq分治通常使用在一些有多个约束条件的场合,为了去掉一些约束条件,而使用。总的来说还是用数据结构维护一些数据,只是用了这种思想后可以大大简化问题(例如把二维的题变成一维之类的),在NlongN时间内解答。而整体二分是二分答案,把问题从一个求值的题,转变成为判定性问题,通过一些途径,判断答案是否能达到某个值。因此整体二分一般应该是用来求最值(或第k大)一类的(我的理解)。
在这里,如果一条路径经过了某个点。 那么它的一个终点(或者两个)必定在以该点为根的子树内。那我们想一下如果两个终点都在以该点为根的子树内,那么该点必定是这两个终点的lca。
而如果两个终点都在以该点为根的子树,而不经过该点,那么该点必定是这两个终点的lca的祖先。因此就可以维护经过某个点的路径有多少。
二分答案K,维护权值大于答案K的路径。假设当前权值大于答案K的路径有num条,而当前经过某个点的路径恰为num条,那么对于这个询问答案必定小于K
#include<cstdio> #include<iostream> #define rep(i,j,k) for(register int i = j; i <= k; i++) #define dow(i,j,k) for(register int i = j; i >= k; i--) #define ez(i,j) for(edge*i = head[j]; i; i=i->next) #define maxn 102333 using namespace std; struct edge{ int to; edge*next; } e[maxn<<1], *pt = e, *head[maxn]; 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++; } inline int read() { int s = 0, t = 1; char c = getchar(); while( !isdigit(c) ) { if( c == '-' ) t = -1; c = getchar(); } while( isdigit(c) ) s = s * 10 + c - 48, c = getchar(); return s * t; } int tot = 0, n, m, dep[maxn], bin[17], fa[maxn][17], t[maxn] = {0}, ti[maxn] = {0}, now = 0, L[maxn], R[maxn]; #define lower(x) (x & -x) #define to i->to inline int query(int x) { int ret = 0; while( x ) { if( ti[x] == now ) ret += t[x]; x -= lower(x); } return ret; } 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); } inline void dfs(int x) { L[x] = ++tot; rep(i,1,16) if( dep[x] >= bin[i] ) fa[x][i] = fa[fa[x][i-1]][i-1]; ez(i,x) if( to != fa[x][0] ) fa[to][0] = x, dep[to] = dep[x] + 1, dfs(to); R[x] = tot; } inline int lca(int x,int y) { if( dep[x] < dep[y] ) swap(x,y); if( dep[x] != dep[y] ) { int dis = dep[x] - dep[y]; rep(i,0,16) if( dis & bin[i] ) dis -= bin[i], x = fa[x][i]; else if( !dis ) break; } if( x == y ) return x; dow(i,16,0) if( fa[x][i] != fa[y][i] ) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } struct node{ int x, y, flag, d; } nod[maxn<<1]; int ans[maxn<<1], t1[maxn<<1], t2[maxn<<1], bel[maxn<<1]; inline void solve(int l,int r,int al,int ar) { if( al == ar ) { rep(i,l,r) ans[bel[i]] = al; return; } int mid = (al + ar) >> 1; int l1 = 0, l2 = 0, num = 0, x, y, z, tot; now++; bool nedl = 0, nedr = 0; rep(i,l,r) if( !nod[bel[i]].flag ) { if( nod[bel[i]].d <= mid ) { t1[++l1] = bel[i]; continue; } t2[++l2] = bel[i]; num++; x = nod[bel[i]].x, y = nod[bel[i]].y, z = lca(x,y); update(L[x],1); update(L[y],1); update(L[z],-1); if( fa[z][0] ) update(L[fa[z][0]],-1); } else if( nod[bel[i]].flag == 1 ) { if( nod[bel[i]].d <= mid ) { t1[++l1] = bel[i]; continue; } t2[++l2] = bel[i]; num--; x = nod[bel[i]].x, y = nod[bel[i]].y, z = lca(x,y); update(L[x],-1); update(L[y],-1); update(L[z],1); if( fa[z][0] ) update(L[fa[z][0]],1); } else { x = nod[bel[i]].x; tot = query(R[x]) - query(L[x]-1); if( tot < num ) t2[++l2] = bel[i], nedr = 1; else t1[++l1] = bel[i], nedl = 1; } rep(i,l,l+l1-1) bel[i] = t1[i-l+1]; rep(i,l+l1,r) bel[i] = t2[i-l-l1+1]; if( nedl ) solve(l,l+l1-1,al,mid); if( nedr ) solve(l+l1,r,mid+1,ar); } int main() { n = read(), m = read(); bin[0] = 1; rep(i,1,16) bin[i] = bin[i-1] << 1; rep(i,1,n-1) add(read(),read()); dfs(1); int mx = 0, x; rep(i,1,m) { nod[i].flag = read(); if( !nod[i].flag ) nod[i].x = read(), nod[i].y = read(), nod[i].d = read(), mx = max(nod[i].d,mx); 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; else nod[i].x = read(); } rep(i,1,m) bel[i] = i, ans[i] = -1; solve(1,m,-1,mx); rep(i,1,m) if( nod[i].flag == 2 ) printf("%d\n", ans[i]); return 0; }
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 |
下面是链剖的。
相关文章推荐
- TCP/IP——TCP协议中提高网络利用率的机制
- ubuntu网络
- 判断有没有网络
- HTTP各个状态返回值
- Android学习记录(十二) http之base/digest鉴权。
- httpclient post发送json数组并解决json乱码问题
- HTTP协议
- Android实时监听网络状态
- Android okHttp上传单张或多张照片
- iOS网络编程(http、socket)
- wireshark解密本地https流量笔记
- Android App 中简易的网络数据处理方法
- Java使用udp传输方式进行网络通信
- tcp协议
- HTTP长连接和短连接原理浅析
- httpd下载地址
- boost::asio::ip::tcp实现网络通信的小例子
- tcp/ip拥塞控制
- tinyhttp源码阅读(注释)
- TCP三次握手