[树链剖分]List wants to travel,Relief grain,hotel加强版,This world need more Zhu
文章目錄
- B:Relief grain
- C:hotel加強(qiáng)版
B:Relief grain
題目
將一段區(qū)間修改的標(biāo)記變成差分,每次都是連續(xù)一段的dfndfndfn序修改
從小到大枚舉dfndfndfn,在一段標(biāo)記的最開頭的dfndfndfn插入,最末尾的dfndfndfn再+1+1+1刪除
就保證了在這連續(xù)的一段都出現(xiàn)了
此時(shí)仍然存在于線段樹中的標(biāo)記就一定是經(jīng)過dfndfndfn序?qū)?yīng)回原樹的節(jié)點(diǎn)
#include <cstdio> #include <vector> #include <cstring> #include <iostream> using namespace std; #define maxn 100005 #define MAX 100000 vector < int > G[maxn], ins[maxn], del[maxn]; int n, m, cnt; int siz[maxn], son[maxn], f[maxn], dep[maxn]; int dfn[maxn], top[maxn], rnk[maxn]; int t[maxn << 2], id[maxn << 2], ans[maxn];void dfs1( int u, int fa ) {son[u] = 0, siz[u] = 1, dep[u] = dep[fa] + 1, f[u] = fa;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == fa ) continue;dfs1( v, u );siz[u] += siz[v];if( ! son[u] || siz[v] > siz[son[u]] )son[u] = v;} }void dfs2( int u, int t ) {top[u] = t, dfn[u] = ++ cnt, rnk[cnt] = u;if( ! son[u] ) return;dfs2( son[u], t );for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == f[u] || v == son[u] ) continue;else dfs2( v, v );} }void pushup( int num ) {if( t[num << 1] >= t[num << 1 | 1] ) t[num] = t[num << 1], id[num] = id[num << 1];else t[num] = t[num << 1 | 1], id[num] = id[num << 1 | 1]; }void build( int num, int l, int r ) {t[num] = id[num] = 0;if( l == r ) {id[num] = l;return;}int mid = ( l + r ) >> 1;build( num << 1, l, mid );build( num << 1 | 1, mid + 1, r ); }void modify( int num, int l, int r, int pos, int v ) {if( l == r ) {t[num] += v;return;}int mid = ( l + r ) >> 1;if( pos <= mid ) modify( num << 1, l, mid, pos, v );else modify( num << 1 | 1, mid + 1, r, pos, v );pushup( num ); }void solve( int u, int v, int w ) {int fu = top[u], fv = top[v];while( fu != fv ) {if( dep[fu] < dep[fv] ) swap( fu, fv ), swap( u, v );ins[dfn[fu]].push_back( w );del[dfn[u] + 1].push_back( w );u = f[fu], fu = top[u];}if( dep[u] < dep[v] ) swap( u, v );ins[dfn[v]].push_back( w );del[dfn[u] + 1].push_back( w ); }int main() {while( ~ scanf( "%d %d", &n, &m ) ) {if( ! n && ! m ) return 0;for( int i = 1;i <= n;i ++ ) G[i].clear(), ins[i].clear(), del[i].clear();cnt = 0;for( int i = 1, u, v;i < n;i ++ ) {scanf( "%d %d", &u, &v );G[u].push_back( v );G[v].push_back( u );}dfs1( 1, 0 );dfs2( 1, 1 );for( int i = 1, x, y, z;i <= m;i ++ ) {scanf( "%d %d %d", &x, &y, &z );solve( x, y, z );}build( 1, 1, MAX );for( int i = 1;i <= n;i ++ ) {for( int j = 0;j < ins[i].size();j ++ )modify( 1, 1, MAX, ins[i][j], 1 );for( int j = 0;j < del[i].size();j ++ )modify( 1, 1, MAX, del[i][j], -1 );if( t[1] ) ans[rnk[i]] = id[1];else ans[rnk[i]] = 0;}for( int i = 1;i <= n;i ++ )printf( "%d\n", ans[i] );}return 0; }C:hotel加強(qiáng)版
鏈接
f[u][j]f[u][j]f[u][j]表示uuu子樹內(nèi)距離uuu為jjj的點(diǎn)個(gè)數(shù)
g[u][j]g[u][j]g[u][j]表示uuu子樹內(nèi)存在兩個(gè)點(diǎn)距離它們的lcalcalca為ddd,且lcalcalca距離uuu為d?jd-jd?j
樹形DPDPDP,是一個(gè)一個(gè)兒子暴力計(jì)算貢獻(xiàn)的
ans+=f[u][j]×g[v][j+1]+g[u][j]×f[v][j?1]ans+=f[u][j]\times g[v][j+1]+g[u][j]\times f[v][j-1]ans+=f[u][j]×g[v][j+1]+g[u][j]×f[v][j?1]
f[u][j]×g[v][j+1]f[u][j]\times g[v][j+1]f[u][j]×g[v][j+1]:在uuu已處理過的兒子中距離uuu為jjj,那么距離現(xiàn)在處理的兒子vvv的距離則為j+1j+1j+1,所以是和以vvv為根的子樹內(nèi)的距離j+1j+1j+1點(diǎn)對進(jìn)行匹配,即g[v][j+1]g[v][j+1]g[v][j+1]
g[u][j]×f[v][j?1]g[u][j]\times f[v][j-1]g[u][j]×f[v][j?1]:在uuu已處理過的兒子中,兩點(diǎn)距離lcalcalca為ddd且lcalcalca距離uuu為d?jd-jd?j的點(diǎn)對數(shù)為g[u][j]g[u][j]g[u][j],能與現(xiàn)在處理的兒子vvv匹配的點(diǎn)個(gè)數(shù)則為f[v][j?1]f[v][j-1]f[v][j?1],f[v][j?1]f[v][j-1]f[v][j?1]的點(diǎn)距離vvv是j?1j-1j?1,距離uuu就是jjj
g[u][j+1]+=f[u][j+1]×f[v][j]g[u][j+1]+=f[u][j+1]\times f[v][j]g[u][j+1]+=f[u][j+1]×f[v][j]:f[u][j+1],f[v][j]f[u][j+1],f[v][j]f[u][j+1],f[v][j]相互匹配的點(diǎn)對是很特殊的,因?yàn)檫@些點(diǎn)對的lcalcalca就是uuu,這些點(diǎn)對距離lcalcalca為j+1j+1j+1,lcalcalca距離uuu的距離為(j+1)?(j+1)=0(j+1)-(j+1)=0(j+1)?(j+1)=0,是符合ggg的定義的
這種特殊的情況統(tǒng)計(jì)后,在uuu往上跳的過程中,uuu不斷降級為兒子,目前特殊的lcalcalca等于根的情況在以后就變成了下邊的一般化的ggg的轉(zhuǎn)移,那個(gè)時(shí)候uuu對根的貢獻(xiàn)就是正確的了,類似于局部最優(yōu)的這種思想,特殊情況往上跳就變成了一般情況
g[u][j?1]+=g[v][j]g[u][j-1]+=g[v][j]g[u][j?1]+=g[v][j]
g[u][j?1]+=g[v][j]g[u][j-1]+=g[v][j]g[u][j?1]+=g[v][j]:g[v][j]g[v][j]g[v][j]是vvv子樹內(nèi)lcalcalca距離vvv為d?jd-jd?j的點(diǎn)對數(shù),這些點(diǎn)對數(shù)的lcalcalca距離uuu則為d?j+1d-j+1d?j+1
f[u][j+1]+=f[v][j]f[u][j+1]+=f[v][j]f[u][j+1]+=f[v][j]
f[u][j+1]+=f[v][j]f[u][j+1]+=f[v][j]f[u][j+1]+=f[v][j]:很好理解,距離uuu為j+1j+1j+1的點(diǎn)個(gè)數(shù)加上距離當(dāng)前正在處理的兒子vvv距離為jjj(距離uuu就為j+1j+1j+1)的個(gè)數(shù)
長鏈剖分即可
長鏈剖分不同于重鏈剖分的無非是不再選用子樹最大的兒子作為重兒子,而是選用長度最長(葉子節(jié)點(diǎn)深度最深)的兒子作為重兒子
用于處理只與深度有關(guān)的樹上問題
#include <cstdio> #include <vector> #include <iostream> using namespace std; #define maxn 100005 #define int long long vector < int > G[maxn]; int n; int dep[maxn], son[maxn]; int *f[maxn], *g[maxn], tmp[maxn << 2], *ip = tmp, ans;void dfs1( int u, int fa ) {for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == fa ) continue;dfs1( v, u );dep[u] = max( dep[u], dep[v] );if( dep[v] > dep[son[u]] ) son[u] = v;}dep[u] = dep[son[u]] + 1; }void dfs2( int u, int fa ) {if( son[u] ) {f[son[u]] = f[u] + 1;g[son[u]] = g[u] - 1;dfs2( son[u], u );}f[u][0] = 1; ans += g[u][0];for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == son[u] || v == fa ) continue;f[v] = ip, ip += ( dep[v] << 1 );g[v] = ip, ip += ( dep[v] << 1 );dfs2( v, u );for( int j = 0;j <= dep[v];j ++ ) {ans += f[u][j] * g[v][j + 1];if( j ) ans += g[u][j] * f[v][j - 1];}for( int j = 0;j <= dep[v];j ++ ) {if( j ) g[u][j - 1] += g[v][j];g[u][j + 1] += f[u][j + 1] * f[v][j];f[u][j + 1] += f[v][j];}} }signed main() {scanf( "%lld", &n );for( int i = 1, u, v;i < n;i ++ ) {scanf( "%lld %lld", &u, &v );G[u].push_back( v );G[v].push_back( u ); }dfs1( 1, 0 );f[1] = ip, ip += ( dep[1] << 1 );g[1] = ip, ip += ( dep[1] << 1 );dfs2( 1, 0 );printf( "%lld\n", ans );return 0; }總結(jié)
以上是生活随笔為你收集整理的[树链剖分]List wants to travel,Relief grain,hotel加强版,This world need more Zhu的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: test2 3-16 2021 模拟赛t
- 下一篇: [树套树] 网络管理