日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[LCT动态树] [NOI2014]魔法森林,[ZJOI2018]历史

發布時間:2023/12/3 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [LCT动态树] [NOI2014]魔法森林,[ZJOI2018]历史 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

[NOI2014] 魔法森林

題目

按照aaa精靈從小到大排序

按順序插入每一條邊

加入第iii條邊后的最小代價為a[i]a[i]a[i]加上從111nnn的所有路徑中最大bbb最小的路徑代價

維護邊權 把邊在LCTLCTLCT中理解為點?——看題解代碼其實就是建虛點

出現環時 選擇最大的bbb刪去即可

/* 一些不成型的想法:貪心??怎么貪兩個呢??——先讓其中一維變成有序再著重貪另一維 私以為此題這樣不可做 passkruskal重構樹——最小生成樹 最小生成樹上的兩點間距離最小LCT維護最小生成樹 a精靈從小到大排序 插入b精靈??邊權改成點權 構造虛點 */ #include <cstdio> #include <iostream> #include <algorithm> using namespace std; #define maxn 150005 #define inf 0x7f7f7f7f struct node {int u, v, a, b; }edge[maxn]; struct LCT {int f, val, rev, id;int son[2]; }t[maxn]; int n, m, ans = inf; int st[maxn], w[maxn];bool cmp( node x, node y ) {return ( x.a == y.a ) ? ( x.b < y.b ) : ( x.a < y.a ); }bool isroot( int x ) {return t[t[x].f].son[0] == x || t[t[x].f].son[1] == x; }void pushup( int x ) {t[x].id = x, t[x].val = w[x];if( t[x].son[0] && t[t[x].son[0]].val > t[x].val ) {t[x].val = t[t[x].son[0]].val;t[x].id = t[t[x].son[0]].id;}if( t[x].son[1] && t[t[x].son[1]].val > t[x].val ) {t[x].val = t[t[x].son[1]].val;t[x].id = t[t[x].son[1]].id;} }void rotate( int x ) {int fa = t[x].f, Gfa = t[fa].f, k = t[fa].son[1] == x;if( isroot( fa ) ) t[Gfa].son[t[Gfa].son[1] == fa] = x;t[x].f = Gfa;if( t[x].son[k ^ 1] ) t[t[x].son[k ^ 1]].f = fa;t[fa].son[k] = t[x].son[k ^ 1];t[x].son[k ^ 1] = fa;t[fa].f = x;pushup( fa );pushup( x ); }void pushdown( int x ) {if( t[x].rev ) {swap( t[x].son[0], t[x].son[1] );if( t[x].son[0] ) t[t[x].son[0]].rev ^= 1;if( t[x].son[1] ) t[t[x].son[1]].rev ^= 1;t[x].rev = 0;} }void splay( int x ) {int top = 0, y = x;st[++ top] = y;while( isroot( y ) ) st[++ top] = y = t[y].f;while( top ) pushdown( st[top --] );while( isroot( x ) ) {int fa = t[x].f, Gfa = t[fa].f;if( isroot( fa ) ) ( ( t[Gfa].son[0] == fa ) ^ ( t[fa].son[0] == x ) ) ? rotate( x ) : rotate( fa );rotate( x );} }void access( int x ) {for( int son = 0;x;son = x, x = t[x].f ) {splay( x );t[x].son[1] = son;pushup( x );} }int FindRoot( int x ) {access( x );splay( x );while( t[x].son[0] ) {pushdown( x );x = t[x].son[0];}return x; }void MakeRoot( int x ) {access( x );splay( x );t[x].rev ^= 1;pushdown( x ); }void link( int x, int y ) {MakeRoot( x );if( FindRoot( y ) != x ) t[x].f = y; }void split( int x, int y ) {MakeRoot( x );access( y );splay( y ); } bool Union( int x, int y ) {MakeRoot( x );return FindRoot( y ) == x; }int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= m;i ++ )scanf( "%d %d %d %d", &edge[i].u, &edge[i].v, &edge[i].a, &edge[i].b );sort( edge + 1, edge + m + 1, cmp );for( int i = 1;i <= m;i ++ ) {w[i + n] = edge[i].b;int u = edge[i].u, v = edge[i].v;if( u == v ) continue;if( Union( u, v ) ) {split( u, v );if( t[v].val <= edge[i].b ) continue;int x = t[v].id;splay( x );t[t[x].son[0]].f = t[t[x].son[1]].f = 0;link( u, i + n ), link( i + n, v );}elselink( u, i + n ), link( i + n, v );if( Union( 1, n ) ) split( 1, n ), ans = min( ans, edge[i].a + t[n].val );}if( ans == inf ) printf( "-1\n" );else printf( "%d\n", ans );return 0; }

[ZJOI2018]歷史

題目

一句話題意: 給出一棵樹,給定每一個點的accessaccessaccess次數,計算輕重鏈切換次數的最大值,帶修改

先不考慮帶修改的問題

uuu,只有在uuu子樹里的點進行accessaccessaccess才會影響答案,并且點獨立,可以分開算

假設uuu的子樹發生了兩次accessaccessaccess,當且僅當這兩次操作的點分屬于uuu兩個不同兒子的子樹中,答案才會+1+1+1

要使答案最大,就是盡量讓所有相鄰發生的崛起都來自不同子樹

相當于有[0,m][0,m][0,m]種顏色,每種顏色有aia_iai?個,最大化相鄰顏色不同的個數

tot=∑i=0mai,maxx=maxi=0m{ai}tot=\sum_{i=0}^ma_i,maxx=max_{i=0}^m\{a_i\}tot=i=0m?ai?,maxx=maxi=0m?{ai?}

答案則為min{t?1,2×(tot?maxx)}min\{t-1,2\times (tot-maxx)\}min{t?1,2×(tot?maxx)}

考慮先放最多的maxxmaxxmaxx個然后插空,對于剩下的pos=tot?maxxpos=tot-maxxpos=tot?maxx

如果pos≤maxx?1pos\le maxx-1posmaxx?1,顯然直接插空,貢獻2×p2\times p2×p

如果pos>maxx?1pos>maxx-1pos>maxx?1,一定可以通過不斷來回插空,達到上界tot?1tot-1tot?1

O(n)O(n)O(n)的時間搜一遍整棵樹即可

現在加上修改

根據2×maxx≥tot+12\times maxx\ge tot+12×maxxtot+1這個分界線處理

為什么這個是分界線??當滿足這個式子的時候,答案一定取后者2×(tot?maxx)2\times (tot-maxx)2×(tot?maxx)

optiopt_iopti?表示iii子樹的操作次數和,如果滿足2×opti≥optfai+12\times opt_i\ge opt_{fa_i}+12×opti?optfai??+1,就把i→fai\rightarrow faifa連為實邊,否則為虛邊

為什么這么定義??因為在這樣的定義下,如果iii子樹內操作jjj加上權值www,其只會影響jjj到根路徑上的點的答案和虛邊關系

因為對于實邊,存在2(opti+w)≥(optfai+w)+12(opt_i+w)\ge (opt_{fa_i}+w)+12(opti?+w)(optfai??+w)+1,即實邊還是實邊

并且答案Δ=2[(optfai+w)?(opti+w)]?2[optfai?opti]=0\Delta =2[(opt_{fa_i}+w)-(opt_i+w)]-2[opt_{fa_i}-opt_i]=0Δ=2[(optfai??+w)?(opti?+w)]?2[optfai???opti?]=0 是不變的

對于怎么找到路徑上的虛邊,就寫LCTLCTLCT,因為這里的操作是類似于accessaccessaccess的,只不過要判斷一下虛實邊之間的變換

ansansans減去點更新前的答案再加上更新后的答案即可

#include <cstdio> #include <vector> using namespace std; #define int long long #define maxn 400005 struct node {int fa, val, sum, thin_edge_sum;int son[2]; }t[maxn]; vector < int > G[maxn]; int n, m, ans;bool isroot( int x ) {return t[t[x].fa].son[0] == x || t[t[x].fa].son[1] == x; }void pushup( int x ) {t[x].sum = t[x].val + t[t[x].son[0]].sum + t[t[x].son[1]].sum + t[x].thin_edge_sum; } void rotate( int x ) {int fa = t[x].fa, Gfa = t[fa].fa, k = t[fa].son[1] == x;if( isroot( fa ) ) t[Gfa].son[t[Gfa].son[1] == fa] = x;t[x].fa = Gfa;if( t[x].son[k ^ 1] ) t[t[x].son[k ^ 1]].fa = fa;t[fa].son[k] = t[x].son[k ^ 1];t[x].son[k ^ 1] = fa;t[fa].fa = x;pushup( fa );pushup( x ); }void splay( int x ) {while( isroot( x ) ) {int fa = t[x].fa, Gfa = t[fa].fa;if( isroot( fa ) )( ( t[Gfa].son[1] == fa ) ^ ( t[fa].son[1] == x ) ) ? rotate( fa ): rotate( x );rotate( x );} }int calc( int x, int tot, int maxx ) {if( t[x].son[1] ) return ( tot - maxx ) * 2;else if( t[x].val * 2 > tot ) return ( tot - t[x].val ) * 2;else return tot - 1; }void modify( int x, int w ) {splay( x );/*如果想要得到某一個點的tot就應該計算splay里面深度≥它的點的val之和也就是將這個點splay到根后這個點的val加上其右子樹的val和*/int tot = t[x].sum - t[t[x].son[0]].sum;int maxx = t[t[x].son[1]].sum;ans -= calc( x, tot, maxx );t[x].val += w, tot += w, t[x].sum += w;if( maxx * 2 < tot + 1 ) { //實邊變虛邊t[x].thin_edge_sum += maxx;t[x].son[1] = 0;}ans += calc( x, tot, maxx );pushup( x );int son;for( x = t[son = x].fa;x;x = t[son = x].fa ) {splay( x );int tot = t[x].sum - t[t[x].son[0]].sum;int maxx = t[t[x].son[1]].sum;ans -= calc( x, tot, maxx );t[x].thin_edge_sum += w, tot += w, t[x].sum += w;if( maxx * 2 < tot + 1 ) {t[x].thin_edge_sum += maxx;t[x].son[1] = 0;maxx = 0;}if( t[son].sum * 2 > tot ) {t[x].thin_edge_sum -= t[son].sum;t[x].son[1] = son;maxx = t[son].sum;}ans += calc( x, tot, maxx );pushup( x );} }void dfs( int u ) {t[u].sum = t[u].val;int pos = 0, maxx = t[u].val;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == t[u].fa ) continue;t[v].fa = u;dfs( v );t[u].sum += t[v].sum;if( t[v].sum > maxx ) maxx = t[v].sum, pos = v;}ans += min( t[u].sum - 1, ( t[u].sum - maxx ) * 2 );if( maxx * 2 >= t[u].sum + 1 ) t[u].son[1] = pos; //實邊 修改是不會更改貢獻的t[u].thin_edge_sum = t[u].sum - t[u].val - t[t[u].son[1]].sum;//所有虛邊的操作數和 以后是會更改貢獻的 }signed main() {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= n;i ++ )scanf( "%lld", &t[i].val );for( int i = 1, u, v;i < n;i ++ ) {scanf( "%lld %lld", &u, &v );G[u].push_back( v );G[v].push_back( u ); }dfs( 1 );printf( "%lld\n", ans );for( int i = 1, x, y;i <= m;i ++ ) {scanf( "%lld %lld", &x, &y );modify( x, y );printf( "%lld\n", ans );}return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的[LCT动态树] [NOI2014]魔法森林,[ZJOI2018]历史的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。