码迷,mamicode.com
首页 > 其他好文 > 详细

4538: [Hnoi2016]网络 链剖 + 堆(优先队列) / 整体二分

时间:2016-05-04 21:10:32      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:

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

上面那个是整体二分的。

下面是链剖的。

 

4538: [Hnoi2016]网络 链剖 + 堆(优先队列) / 整体二分

标签:

原文地址:http://www.cnblogs.com/83131yyl/p/5459614.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!