[HNOI2015] 接水果(倍增 + 整体二分)
problem
luogu-P3242
solution
本題的難點在于如何判定路徑之間是否覆蓋。
這里我們嘗試樹常見的 dfs\text{dfs}dfs 序。
考慮 x?yx-yx?y 路徑如果要覆蓋 u?vu-vu?v 路徑需要滿足怎樣的條件。
以下均假設 dfs(u)<dfs(v),dfs(x)<dfs(y)dfs(u)<dfs(v),dfs(x)<dfs(y)dfs(u)<dfs(v),dfs(x)<dfs(y)。
-
lca(u,v)≠ulca(u,v)\ne ulca(u,v)?=u。
xxx 必須是 uuu 子樹內的一點,yyy 必須是 vvv 子樹內的一點。
我們記點 iii 子樹內的 dfs\text{dfs}dfs 序列對應連續區間為 [l(i),r(i)][l(i),r(i)][l(i),r(i)]。
則要 l(u)≤l(x)≤r(u)∧l(v)≤l(y)≤r(v)l(u)\le l(x)\le r(u)\wedge l(v)\le l(y)\le r(v)l(u)≤l(x)≤r(u)∧l(v)≤l(y)≤r(v)。
其實,我們可以將 (l(x),l(y))(l(x),l(y))(l(x),l(y)) 當成一個點的坐標;
將 (l(u),l(v))?(r(u),r(v))(l(u),l(v))-(r(u),r(v))(l(u),l(v))?(r(u),r(v)) 看作左下角為 (l(u),l(v))(l(u),l(v))(l(u),l(v)) 右上角為 (r(u),r(v))(r(u),r(v))(r(u),r(v)) 的矩陣。
發現這個點就是落在這個矩陣內的。
所以問題就是某個點被若干個矩形包含,求這里面的權值第 kkk 小的矩陣。
-
lca(u,v)=ulca(u,v)=ulca(u,v)=u。
xxx 必須屬于 u→vu\rightarrow vu→v 走的第一個點子樹外的部分,yyy 仍是 vvv 子樹內一點。
即,假設 uuu 到 vvv 的路徑經過的 uuu 的兒子為 www,即路徑為 u→w→…vu\rightarrow w\rightarrow\dots vu→w→…v。
則要 1≤l(x)<l(w)∨r(w)<l(x)≤n1\le l(x)<l(w)\vee r(w)<l(x)\le n1≤l(x)<l(w)∨r(w)<l(x)≤n,l(v)≤l(y)≤r(v)l(v)\le l(y)\le r(v)l(v)≤l(y)≤r(v)。
兩個條件是獨立的。
- 1≤l(x)<l(w)1\le l(x)<l(w)1≤l(x)<l(w),對應矩陣 (1,l(v))?(l(w)?1,r(v))(1,l(v))-(l(w)-1,r(v))(1,l(v))?(l(w)?1,r(v))。
- r(w)<l(x)≤nr(w)<l(x)\le nr(w)<l(x)≤n,對應矩陣 (l(v),r(w)+1)?(r(v),n)(l(v),r(w)+1)-(r(v),n)(l(v),r(w)+1)?(r(v),n)。
找 lcalcalca 和某個點的下面一個點,可以倍增,可以樹鏈剖分,好像是鏈剖分快點,但我們不需要卡這么點常。
對于一個詢問,我們可以二分答案,然后把所有權值不大于答案的矩陣激活,統計包含該詢問對應點的被激活的矩陣有多少個,根據和 kkk 的關系移動二分端點。
所以我們可以對所有詢問套一個整體二分。
而這個矩陣激活我們就可以掃描線地做。
對于 (x1,y1)?(x2,y2)(x1,y1)-(x2,y2)(x1,y1)?(x2,y2) 的矩陣,我們以 xxx 軸做掃描線從左往右掃。
變成 (y1,y2,x1,1)(y1,y2,x1,1)(y1,y2,x1,1) 在 x=x1x=x1x=x1 的時候把 [y1,y2]+1[y1,y2]+1[y1,y2]+1,(y1,y2,x2+1,?1)(y1,y2,x2+1,-1)(y1,y2,x2+1,?1) 在 x=x2x=x2x=x2 的時候把 [y1,y2]?1[y1,y2]-1[y1,y2]?1。
數據結構仍然使用樹狀數組。
由于使用的是掃描線,所以一開始要將所有詢問按 xxx 排序,并且將矩陣按權值排序。
且每次整體二分內部都要將權值屬于 [l,mid][l,mid][l,mid] 區間的矩陣重新按 xxx 軸排序。
時間復雜度 O(nlog?2n)O(n\log^2n)O(nlog2n) 。
code
#include <cstdio> #include <vector> #include <iostream> #include <algorithm> using namespace std; #define maxn 40005vector < int > G[maxn]; int dep[maxn], st[maxn], ed[maxn]; int f[maxn][16]; int cnt;void dfs( int u, int fa ) {dep[u] = dep[fa] + 1, f[u][0] = fa, st[u] = ++ cnt;for( int i = 1;i < 16;i ++ ) f[u][i] = f[f[u][i - 1]][i - 1];for( int v : G[u] ) if( v ^ fa ) dfs( v, u );ed[u] = cnt; } int lca( int u, int v ) {if( dep[u] < dep[v] ) swap( u, v );for( int i = 15;~ i;i -- ) if( dep[f[u][i]] >= dep[v] ) u = f[u][i];if( u == v ) return u;for( int i = 15;~ i;i -- ) if( f[u][i] ^ f[v][i] ) u = f[u][i], v = f[v][i];return f[u][0]; } int top( int u, int d ) {for( int i = 15;~ i;i -- ) if( d >> i & 1 ) u = f[u][i]; return u; }int cntg, n, m1, m2; int ans[maxn]; struct scan { int l, r, x, op; }s[maxn << 2]; struct matrix { int x1, y1, x2, y2, w; }g[maxn << 2]; struct query { int x, y, k, id; }q[maxn], L[maxn], R[maxn];namespace BIT {int t[maxn];void add( int l, int r, int k ) {for( ;l <= n;l += l & -l ) t[l] += k;for( ;r <= n;r += r & -r ) t[r] -= k;}int ask( int x ) {int sum = 0;for( ;x;x -= x & -x ) sum += t[x];return sum;} }void solve( int l, int r, int ql, int qr ) {if( ql > qr ) return;if( l == r ) { for( int i = ql;i <= qr;i ++ ) ans[q[i].id] = g[l].w; return; }int mid = l + r >> 1; cnt = 0;for( int i = l;i <= mid;i ++ ) {s[++ cnt] = (scan){ g[i].y1, g[i].y2, g[i].x1, 1 };s[++ cnt] = (scan){ g[i].y1, g[i].y2, g[i].x2 + 1, -1 };}sort( s + 1, s + cnt + 1, []( scan a, scan b ) { return a.x < b.x; } );int cntl = 0, cntr = 0, j = 1;for( int i = ql;i <= qr;i ++ ) {for( ;j <= cnt and s[j].x <= q[i].x;j ++ ) BIT :: add( s[j].l, s[j].r + 1, s[j].op );int k = BIT :: ask( q[i].y );if( q[i].k <= k ) L[++ cntl] = q[i];else q[i].k -= k, R[++ cntr] = q[i]; }for( int i = 1;i < j;i ++ ) BIT :: add( s[i].l, s[i].r + 1, -s[i].op );//不一定把l~mid的矩陣都掛在了樹上的for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = L[i];for( int i = 1;i <= cntr;i ++ ) q[ql + cntl + i - 1] = R[i];solve( l, mid, ql, ql + cntl - 1 );solve( mid + 1, r, ql + cntl, qr ); }int main() {scanf( "%d %d %d", &n, &m1, &m2 );for( int i = 1, u, v;i < n;i ++ ) {scanf( "%d %d", &u, &v );G[u].push_back( v );G[v].push_back( u );}dfs( 1, 0 );for( int i = 1, u, v, w;i <= m1;i ++ ) {scanf( "%d %d %d", &u, &v, &w );if( st[u] > st[v] ) swap( u, v );int x = lca( u, v );if( x ^ u ) g[++ cntg] = (matrix){ st[u], st[v], ed[u], ed[v], w };else {x = top( v, dep[v] - dep[u] - 1 );if( st[x] ^ 1 ) g[++ cntg] = (matrix){ 1, st[v], st[x] - 1, ed[v], w };if( ed[x] ^ n ) g[++ cntg] = (matrix){ st[v], ed[x] + 1, ed[v], n, w };}}for( int i = 1, u, v, k;i <= m2;i ++ ) {scanf( "%d %d %d", &u, &v, &k );if( st[u] > st[v] ) swap( u, v );q[i] = (query){ st[u], st[v], k, i };}sort( q + 1, q + m2 + 1, [](query a, query b) { return a.x < b.x; } );sort( g + 1, g + cntg + 1, [](matrix a, matrix b){ return a.w < b.w; } );solve( 1, cntg, 1, m2 );for( int i = 1;i <= m2;i ++ ) printf( "%d\n", ans[i] );return 0; }總結
以上是生活随笔為你收集整理的[HNOI2015] 接水果(倍增 + 整体二分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020中专中医备案(医考备案中专)
- 下一篇: 磊科无线路由器怎么设置 设置方法[图文教