zoukankan      html  css  js  c++  java
  • 线段树合并

    \(P3521\)

    首先题目要我们求的是叶子节点的前序遍历\((\)逆序对数\()\)最小值。

    不难想到一个性质,一个点的子树内部的答案不会因为交换左右子树而改变。

    所以我们可以贪心 \(->\) 对于每个点的子树都弄出最优(逆序对)。

    所以实际上我们要求的是:

    \(1.\)左-右的答案。

    \(2.\)右-左的答案。

    较为暴力的想法去求答案就是:(以求左-右为例)

    如果要合并某一点的左右子树答案,我们就需要得知权值。

    可以对每个点开一颗权值线段树,那么如果我需要得知某一个点的左-右答案怎么来呢?我们可以暴力的去查找每一个权值。假设其为\(x\)

    那么左-右在x处产生的答案就是:

    左子树:\((1-x)*\) 右子树:\((x-n)\)的数量。

    然后我们可以在权值线段树内递归的处理每一个\(x\),同时合并所有的权值。

    这貌似就是线段树合并\(QAQ()\)然而良心出题人让我们不得不把某一个点的右子树合并到左子树里面去\((\)否则空间开销难以接受\(QAQ)\)

    每次贪心取其中一种答案加上就好了

    #include<bits/stdc++.h>
    using namespace std;
    int read() {
    	char cc = getchar(); int cn = 0, flus = 1;
    	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    	return cn * flus;
    }
    const int N = 400000 + 5; 
    #define LL long long
    #define ls(x) tr[x].l
    #define rs(x) tr[x].r
    struct Tr{
    	int val, l, r;
    }tr[N * 25];
    int maxv, n, cot, cnt, root[N];
    LL Ans, ans1, ans2;
    void update( int &x, int l, int r, int wh ) {
    	
    	if( !x ) x = ++ cot;
    	++ tr[x].val;
    	if( l == r ) return ;
    	int mid = ( l + r ) >> 1;
    	if( mid >= wh ) update( ls(x), l, mid, wh );
    	else update( rs(x), mid + 1, r, wh );
    }
    void merge( int &lx, int rx ) {
    	if( !lx || !rx ) { lx = lx + rx; return ; }
    	tr[lx].val = tr[lx].val + tr[rx].val;
    	ans1 += 1ll * tr[tr[lx].r].val * tr[tr[rx].l].val;
    	ans2 += 1ll * tr[tr[rx].r].val * tr[tr[lx].l].val;
    	merge( tr[lx].l, tr[rx].l );
    	merge( tr[lx].r, tr[rx].r );
    }
    void dfs( int &x ) {
    	int t, ls, rs;
    	t = read();
    	if( t != 0 ) {
    		x = ++ cnt, update( root[x], 1, n, t );
    		return ;
    	}
    	dfs( ls ), dfs( rs ), ans1 = ans2 = 0, x = ls;
    	merge( root[x], root[rs] );
    	Ans += min( ans1, ans2 );
    }
    signed main()
    {
    	n = read();
    	dfs(root[0]);
    	printf("%lld\n", Ans );
    	return 0;
    }
    



    \(P4556[Vani\)有约会\(]\)

    比较朴素的想法:树上差分+权值线段树合并

    这道题给了一点点小小的注意:

    \((1):\)注意权值线段树的范围\(QWQ\)

    \((2):\)合并的时候注意优先级。

    记录一点点思路,当时 \(55\) 分的代码是这样的:

    void merge( int &lx, int rx, int l, int r ) {
    	if( l == r ) { tr[lx].mx += tr[rx].mx;  return ; }
    	if( !lx || !rx ) { lx = lx + rx; return ; }
    	
    	int mid = ( l + r ) >> 1;
    	merge( ls(lx), ls(rx), l, mid ), merge( rs(lx), rs(rx), mid + 1, r );
    	tr[lx].mx = max( tr[ls(lx)].mx, tr[rs(lx)].mx );
    }
    

    后来的 \(100\) 分代码是这样:

    void merge( int &lx, int rx, int l, int r ) {
    	if( !lx || !rx ) { lx = lx + rx; return ; }
    	if( l == r ) { tr[lx].mx += tr[rx].mx;  return ; }
    	int mid = ( l + r ) >> 1;
    	merge( ls(lx), ls(rx), l, mid ), merge( rs(lx), rs(rx), mid + 1, r );
    	tr[lx].mx = max( tr[ls(lx)].mx, tr[rs(lx)].mx );
    }
    

    注意要先判断点是否存在再合并,不然可能会合并到 \(0\) (空)节点处。

    以及给自己的一点小想法?

    用权值线段树查出现次数最多的数可以通过不断的比对左右儿子的\(max\)来二分找。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int read() {
    	char cc = getchar(); int cn = 0, flus = 1;
    	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
    	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
    	return cn * flus;
    }
    const int N = 200000 + 5;
    #define ls(x) tr[x].l
    #define rs(x) tr[x].r
    #define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
    #define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
    #define drep( i, t, s ) for( register int i = t; i >= s; -- i )
    struct E {
    	int to, next;
    }e[N * 2];
    struct Tree {
    	int l, r, mx;
    }tr[N * 25];
    int fath[N][20], n, m, head[N], root[N], dep[N], cnt, num, Maxc, ans[N];
    void add( int x, int y ) {
    	e[++ cnt] = (E){ y, head[x] }, head[x] = cnt;
    	e[++ cnt] = (E){ x, head[y] }, head[y] = cnt;
    }
    void dfs( int x, int fa ) {
    	fath[x][0] = fa, dep[x] = dep[fa] + 1;
    	rep( i, 1, 19 ) fath[x][i] = fath[fath[x][i - 1]][i - 1];
    	Next( i, x ) {
    		int v = e[i].to;
    		if( v == fa ) continue;
    		dfs( v, x ); 
    	}
    }
    int LCA( int x, int y ) {
    	if( dep[x] < dep[y] ) swap( x, y );
    	drep( i, 19, 0 ) if( dep[fath[x][i]] >= dep[y] ) x = fath[x][i];
    	drep( i, 19, 0 ) if( fath[x][i] != fath[y][i] ) x = fath[x][i], y = fath[y][i];
    	return ( x == y ) ? x : fath[x][0];
    }
    
    void update( int &x, int ll, int rr, int wh, int add ) {
    	if( !x ) x = ++ num ;
    	if( ll == rr ) { tr[x].mx += add; return ; }
    	int mid = ( ll + rr ) >> 1;
    	if( mid >= wh ) update( ls(x), ll, mid, wh, add );
    	else update( rs(x), mid + 1, rr, wh, add );
    	
    	tr[x].mx = max( tr[ls(x)].mx, tr[rs(x)].mx );
    } 
    void merge( int &lx, int rx, int l, int r ) {
    	if( !lx || !rx ) { lx = lx + rx; return ; }
    	if( l == r ) { tr[lx].mx += tr[rx].mx;  return ; }
    	int mid = ( l + r ) >> 1;
    	merge( ls(lx), ls(rx), l, mid ), merge( rs(lx), rs(rx), mid + 1, r );
    	tr[lx].mx = max( tr[ls(lx)].mx, tr[rs(lx)].mx );
    }
    int query_Mx( int x, int l, int r ) {
    	int mid = ( l + r ) >> 1;
    	if( !tr[x].mx ) return 0;
    	while( l != r ) {
    		mid = ( l + r ) >> 1;
    		if( tr[ls(x)].mx >= tr[rs(x)].mx ) r = mid, x = ls(x);
    		else l = mid + 1, x = rs(x);
    	}
    	return l;
    }
    void solve( int x, int y, int z ) {
    	int lca =  LCA( x, y );
    	update( root[lca], 1, Maxc, z, -1 );
    	if( fath[lca][0] ) update( root[fath[lca][0]], 1, Maxc, z, -1 );
    	update( root[x], 1, Maxc, z, 1 );
    	update( root[y], 1, Maxc, z, 1 );
    }
    void dfs_merge( int x, int fa ) {
    	Next( i, x ) {
    		int v = e[i].to;
    		if( v == fa ) continue;
    		dfs_merge( v, x );
    		merge( root[x], root[v], 1, Maxc );
    	}
    	ans[x] = query_Mx( root[x], 1, Maxc );
    } 
    signed main()
    {
    	n = read(), m = read();
    	int x, y, z; Maxc = 100000;
    	rep( i, 1, n - 1 ) x = read(), y = read(), add( x, y );
    	dfs( 1, 0 );
    	rep( i, 1, m ) x = read(), y = read(), z = read(), solve( x, y, z ) ;
    	dfs_merge( 1, 0 );
    	rep( i, 1, n ) printf("%d\n", ans[i] );
    	return 0;
    }
    
    
  • 相关阅读:
    Spring MVC 3 深入总结
    精益之识别和消除研发过程中浪费的思路和模式
    怎样区分直连串口线和交叉串口线?
    UVA 10557 XYZZY
    概率论 —— 分析计算机系统和网络的可靠性和通用性
    概率论 —— 分析计算机系统和网络的可靠性和通用性
    Sift中尺度空间、高斯金字塔、差分金字塔(DOG金字塔)、图像金字塔
    鲁迅先生的话
    鲁迅先生的话
    辛词
  • 原文地址:https://www.cnblogs.com/Soulist/p/10596132.html
Copyright © 2011-2022 走看看