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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

数据结构之线段树合并——永无乡,Lomsat gelral,Tree Rotations,Tree Rotations Escape Through Leaf

發布時間:2023/12/3 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构之线段树合并——永无乡,Lomsat gelral,Tree Rotations,Tree Rotations Escape Through Leaf 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • [HNOI2012]永無鄉
  • Lomsat gelral
  • 「POI2011 R2 Day2」旋轉樹木 Tree Rotations
  • Escape Through Leaf

線段樹合并與 fhq-treap合并很類似,也是將兩個不同根的線段樹暴力合并

至于時間復雜度,線段樹合并一次是可以達到O(n)O(n)O(n)的,但是大家都是算平攤復雜度的,平攤后就是log?\loglog級別了

int merge( int x, int y, int l, int r ) {if( ! x or ! y ) return x + y;if( l == r ) { //操作一下return x;}int mid = ( l + r ) >> 1;t[x].l = merge( t[x].l, t[y].l, l, mid );t[x].r = merge( t[x].r, t[y].r, mid + 1, r );//再合并操作一下return x; }

如果是樹的線段樹合并

就是先操作完兒子,再插入父親

void dfs( int u, int fa ) {for( auto v : G[u] ) {if( v == fa ) continue;else dfs( v, u );root[u] = merge( root[u], root[v], 1, n );}//可能還有點操作modify( root[u], 1, n, [] ); }

[HNOI2012]永無鄉

BZOJ2733

初始每個島嶼自己是一個連通塊,自己單開一個權值線段樹(使用動態開點)

然后就很暴力,利用并查集可以判斷那兩個島嶼是否已經在一個連通塊

不在就直接線段樹合并,在就不管

查詢就找到其所在連通塊的線段樹,直接查就是了

#include <cstdio> #define maxn 100005 struct node { int l, r, id, tot; }t[maxn * 50]; int f[maxn], root[maxn]; int n, m, cnt;int find( int x ) { return x == f[x] ? x : f[x] = find( f[x] ); }void modify( int &now, int l, int r, int pos, int id ) {if( ! now ) now = ++ cnt;if( l == r ) { t[now].id = id, t[now].tot ++; return; }int mid = ( l + r ) >> 1;if( pos <= mid ) modify( t[now].l, l, mid, pos, id );else modify( t[now].r, mid + 1, r, pos, id );t[now].tot = t[t[now].l].tot + t[t[now].r].tot; }int merge( int x, int y, int l, int r ) {if( ! x or ! y ) return x + y;if( l == r ) { if( t[y].id ) t[x].id = t[y].id;t[x].tot += t[y].tot;return x;}int mid = ( l + r ) >> 1;t[x].l = merge( t[x].l, t[y].l, l, mid );t[x].r = merge( t[x].r, t[y].r, mid + 1, r );t[x].tot = t[t[x].l].tot + t[t[x].r].tot;return x; }int query( int now, int k, int l, int r ) {if( ! now or t[now].tot < k ) return -1;int mid = ( l + r ) >> 1;if( l == r ) return t[now].id;if( t[t[now].l].tot >= k ) return query( t[now].l, k, l, mid );else return query( t[now].r, k - t[t[now].l].tot, mid + 1, r ); }int main() {scanf( "%d %d", &n, &m );for( int i = 1, x;i <= n;i ++ ) {scanf( "%d", &x );modify( root[i], 1, n, x, i );f[i] = i;}for( int i = 1, u, v;i <= m;i ++ ) {scanf( "%d %d", &u, &v );u = find( u );v = find( v );f[v] = u;root[u] = merge( root[u], root[v], 1, n );}char opt[10]; int x, y, Q;scanf( "%d", &Q );while( Q -- ) {scanf( "%s %d %d", opt, &x, &y );if( opt[0] == 'B' ) {x = find( x );y = find( y );if( x == y ) continue;else f[y] = x, root[x] = merge( root[x], root[y], 1, n );}else {x = find( x );printf( "%d\n", query( root[x], y, 1, n ) );}}return 0; }

Lomsat gelral

CF600E

dfs先操作兒子,兒子操作完后并到父親線段樹內

線段樹維護區間顏色出現最大值,和顏色編號和,基操

#include <cstdio> #include <vector> using namespace std; #define maxn 100005 #define int long long vector < int > G[maxn]; struct node { int l, r, Max, sum; }t[maxn * 50]; int cnt, n; int c[maxn], root[maxn];void pushup( int now ) {if( t[t[now].l].Max > t[t[now].r].Max ) {t[now].Max = t[t[now].l].Max;t[now].sum = t[t[now].l].sum;}else if( t[t[now].l].Max < t[t[now].r].Max ) {t[now].Max = t[t[now].r].Max;t[now].sum = t[t[now].r].sum;}else {t[now].Max = t[t[now].l].Max;t[now].sum = t[t[now].l].sum + t[t[now].r].sum;} }void modify( int &now, int l, int r, int pos ) {if( ! now ) now = ++ cnt;if( l == r ) { t[now].Max ++, t[now].sum = l; return; }int mid = l + r >> 1;if( pos <= mid ) modify( t[now].l, l, mid, pos );else modify( t[now].r, mid + 1, r, pos );pushup( now ); }int merge( int now, int lst, int l, int r ) {if( ! now or ! lst ) return now + lst;if( l == r ) { t[now].Max += t[lst].Max; return now; }int mid = l + r >> 1;t[now].l = merge( t[now].l, t[lst].l, l, mid );t[now].r = merge( t[now].r, t[lst].r, mid + 1, r );pushup( now );return now; }void dfs( int u, int fa ) {for( auto v : G[u] ) {if( v == fa ) continue;else dfs( v, u );root[u] = merge( root[u], root[v], 1, n );}modify( root[u], 1, n, c[u] ); }signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ ) {scanf( "%lld", &c[i] );root[i] = i;cnt ++;}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, 0 );for( int i = 1;i <= n;i ++ )printf( "%lld ", t[root[i]].sum );return 0; }

「POI2011 R2 Day2」旋轉樹木 Tree Rotations

LOJ2163

性質:考慮相同長度的不交的兩段x,yx,yx,y要計算逆序對,則xxx再分成相同長度不交的兩段a,ba,ba,b

不管是a,ba,ba,b還是b,ab,ab,axxxyyy的逆序對(一個來自xxx,一個來自yyy)數量不會變

因為換不換,都是在yyy前面

這就可以用線段樹維護,這個性質意味著兒子換不換,對父親與其他父親間的計算沒有影響

所以建立權值線段樹,直接對于每一層線段樹合并

先計算左右兒子不換,和換的逆序對,再合并

合并完后,選擇更小的方案

#include <cstdio> #include <iostream> using namespace std; #define maxn 200005 #define int long long struct node { int l, r, ans; }t[maxn * 50]; int cnt, nxd1, nxd2;void modify( int &now, int l, int r, int pos ) {if( ! now ) now = ++ cnt;t[now].ans ++;if( l == r ) return;int mid = l + r >> 1;if( pos <= mid ) modify( t[now].l, l, mid, pos );else modify( t[now].r, mid + 1, r, pos ); }int merge( int x, int y, int l, int r ) {if( ! x or ! y ) return x + y;t[x].ans += t[y].ans;if( l == r ) return x;int mid = l + r >> 1;nxd1 += t[t[x].r].ans * t[t[y].l].ans;nxd2 += t[t[x].l].ans * t[t[y].r].ans;t[x].l = merge( t[x].l, t[y].l, l, mid );t[x].r = merge( t[x].r, t[y].r, mid + 1, r );return x; }int n, x, ans, node; int root[maxn]; void dfs( int &k ) {scanf( "%lld", &x );if( x ) {k = ++ node;modify( root[k], 1, n, x );return;}int l, r;dfs( l );dfs( r );nxd1 = 0;nxd2 = 0;merge( root[l], root[r], 1, n );ans += min( nxd1, nxd2 );k = l; }signed main() {scanf( "%lld", &n );dfs( n );printf( "%lld\n", ans );return 0; }

Escape Through Leaf

CF932F

首先,有個簡單的n2n^2n2dpdpdp:設dpu:udp_u:udpu?:u的答案,則dpu=min?{dpv+av?bu}v∈sonudp_u=\min\{dp_v+a_v*b_u\}\quad v\in son_udpu?=min{dpv?+av??bu?}vsonu?

這完完全全就是李超線段樹的長相

直接dfs樹合并李超線段樹

考慮兩條直線的合并,t[x]表示原先已有的直線,now表示這一次新加的直線

  • 新直線的斜率更大,判斷在mid處兩條直線的值大小

    • 新直線更小

      原直線可能在mid右邊有貢獻,繼續修改右兒子,并且mid處的直線被更新成新直線

    • 新直線更大

      新直線可能在mid左邊有貢獻,繼續修改左兒子,并且mid處的直線不會被更新

  • 新直線的斜率更小,判斷在mid處兩條直線的值大小

    • 新直線更小

      原直線可能在mid左邊有貢獻,繼續修改左兒子,并且mid處的直線被更新成新直線

    • 新直線更大

      新直線可能在mid右邊有貢獻,繼續修改右兒子,并且mid處的直線不會被更新

  • 斜率相同

    直接判斷截距的大小,選擇截距小的直線,直接覆蓋完

#include <cstdio> #include <vector> #include <iostream> using namespace std; #define maxn 100005 #define int long long vector < int > G[maxn]; struct node { int k, b; }t[maxn * 50]; int a[maxn], b[maxn], root[maxn], ans[maxn], lson[maxn * 50], rson[maxn * 50]; int n, cnt;int calc( node l, int x ) { return l.k * x + l.b; }void modify( int &x, int l, int r, node now ) {if( ! x ) { t[x = ++ cnt] = now; return; }if( l == r ) { t[x] = calc( t[x], l ) < calc( now, l ) ? t[x] : now; return; }int mid = ( l + r ) >> 1;if( now.k > t[x].k ) {if( calc( now, mid ) <= calc( t[x], mid ) ) modify( rson[x], mid + 1, r, t[x] ), t[x] = now;else modify( lson[x], l, mid, now );}else if( now.k < t[x].k ) {if( calc( now, mid ) <= calc( t[x], mid ) )modify( lson[x], l, mid, t[x] ), t[x] = now;elsemodify( rson[x], mid + 1, r, now );}else if( now.b < t[x].b ) t[x] = now; }int query( int now, int l, int r, int x ) {if( ! now ) return 1e18;if( l == r ) return calc( t[now], x );int mid = ( l + r ) >> 1, ret;if( x <= mid ) ret = query( lson[now], l, mid, x );else ret = query( rson[now], mid + 1, r, x );return min( ret, calc( t[now], x ) ); }int merge( int x, int y, int l, int r ) {if( ! x or ! y ) return x + y;if( l == r ) { return calc( t[x], l ) < calc( t[y], l ) ? x : y; }int mid = ( l + r ) >> 1;lson[x] = merge( lson[x], lson[y], l, mid );rson[x] = merge( rson[x], rson[y], mid + 1, r );modify( x, l, r, t[y] );return x; }void dfs( int u, int fa ) {bool flag = 0;for( auto v : G[u] ) {if( v == fa ) continue;else dfs( v, u );flag = 1;root[u] = merge( root[u], root[v], -1e5, 1e5 );}node line;line.k = b[u];if( flag ) ans[u] = line.b = query( root[u], -1e5, 1e5, a[u] );else ans[u] = line.b = 0;modify( root[u], -1e5, 1e5, line ); }signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &b[i] );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, -1 );for( int i = 1;i <= n;i ++ ) printf( "%lld ", ans[i] );return 0; }

總結

以上是生活随笔為你收集整理的数据结构之线段树合并——永无乡,Lomsat gelral,Tree Rotations,Tree Rotations Escape Through Leaf的全部內容,希望文章能夠幫你解決所遇到的問題。

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