zoukankan      html  css  js  c++  java
  • 数据结构总结——线段树

    此博客仅为总结,不适合新手

    线段树

    线段树(segment tree)是一种维护区间信息的数据结构, 如下图

    其特点在于运用了二分的思想,将一段长度为n的区间分开为logn层,然后我们每次查询区间时最多会访问logn个区间

    下面代码风格可能由于历史悠久导致不同,所以最好不要学习我的代码,完整的线段树题目可以看我的csdn博客,当时我搞这个线段树还是很恼火的

     初级应用——序列操作

    例一
    给你N个数,有两种操作 1:给区间[a,b]的所有数都增加X 2:询问第i个数是什么?

    怀念丁神idy002教我们的指针版线段树

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 100000+10;
    
    struct Node {
        ll flag,sum;
        Node *ls, *rs;
    }pool[N*2+5], *tail = pool, *root;
    
    int n,q;
    int aa[N];
    
    Node *build( int lf, int rg ) {
        Node *nd = ++tail;
        if( rg == lf ) {
            nd->sum = aa[lf];
            nd->flag = 0;
        } else {
            int mid = (lf + rg) >> 1;
            nd->ls = build( lf, mid );
            nd->rs = build( mid+1, rg );
            nd->sum = nd->ls->sum + nd->rs->sum;
            nd->flag = 0;
        }
        return nd;
    }
    void modify( Node *nd, int lf, int rg, int L, int R, int delta ) {
        if( L <= lf && rg <= R ) {
            nd->flag += delta;
            return;
        }
        int mid = (lf + rg) >> 1;
        if( L <= mid ) modify( nd->ls, lf, mid, L, R, delta );
        if( R > mid) modify( nd-> rs, mid+1, rg, L, R, delta );
        nd->sum = nd->ls->sum + nd->rs->sum + nd->ls->flag*(mid-lf+1) + nd->rs->flag*(rg-mid);
    }
    ll query( Node *nd, int lf, int rg, int L, int R ) {
        if( L <= lf && rg <= R ) {
            return nd->sum + nd->flag * (rg - lf + 1);
        }
        int mid = (lf + rg) >> 1;
        ll rt = (min(R,rg) - max(lf,L) + 1) * nd->flag;
        if( L <= mid ) rt += query( nd->ls, lf, mid, L, R );
        if( R > mid ) rt += query( nd->rs, mid+1, rg, L, R );
        return rt;
    }
    int main() {
        scanf("%d", &n);
        for( int i = 1; i <= n; i++ ) scanf("%d", &aa[i]);
        root = build( 1, n );
        scanf( "%d", &q );
        while(q--) {
            int yu;
            scanf("%d", &yu);
            if(yu == 1) {
                int a,b,c;
                scanf("%d%d%d", &a, &b, &c);
                modify( root, 1, n, a, b, c );
            } else {
                int x;
                scanf("%d", &x);
                cout << query( root, 1, n, x, x ) << endl;
            }
        }
        return 0;
    }

    例二

    给你一个序列:a1 a2 a3 : : : an,有m 个操作,操作如下:
    • modify l r x  将区间[l; r] 中的每个数修改为x
    • change l r x 将区间[l; r] 中的每个数加上x
    • query l r 询问区间[l; r] 中的和

    较上一题略有提升,需要维护多个标记

    切记这道题标记的顺序

    #include<cstdio>
    using namespace std;
    typedef long long ll;
    const int N = 1000000 + 5;
    inline int read() {
        int x = 0, f = 1;char ch = getchar();
        while( ch < '0' || ch > '9'){if(ch == '-')f=-1;ch=getchar();}
        while(ch>='0' && ch <='9'){x = x*10+ch-'0';ch = getchar();}
        return x*f;
    }
    struct Node;
    void modify( Node *nd, int lf, int rg, int L, int R, ll value );
    void change( Node *nd, int lf, int rg, int L, int R, ll delta );
    struct Node{
    	ll sum;
    	ll value,delta;
    	Node *ls, *rs;
    	int type;
    	void update(){ sum = ls->sum + rs->sum; }
    	void pushdown( int lf, int rg ){
    		if( type == 0 ) return;
    		int mid = (lf+rg) >> 1;
    		if( type == 1 ){
    			change( ls, lf, mid, lf, mid, delta );
    			change( rs, mid+1, rg, mid+1, rg, delta );
    			type = 0;
    		} else{
    			modify( ls, lf, mid, lf, mid, value );
    			modify( rs, mid+1, rg, mid+1, rg, value );
    			type = 0;
    		}
    	}
    }pool[N*2],*tail = pool,*root;
    int m,n,aa[N];
    Node *build( int lf, int rg ){
    	Node *nd = ++tail;
    	if( lf == rg ){
    		nd->sum = aa[lf];
    		nd->type = 0;
    	} else{
    		int mid = (lf+rg) >> 1;
    		nd->ls = build( lf, mid );
    		nd->rs = build( mid+1, rg);
    		nd->type = 0;
    		nd->update();
    	}
    	return nd;
    }
    void modify( Node *nd, int lf, int rg, int L, int R, ll value ){
    	if( lf >= L && rg <= R ){
    		nd->sum = (ll)(rg-lf+1)*value;
    		nd->type = 2; nd->value = value; return ;
    	}
    	int mid = (lf+rg) >> 1;
    	nd->pushdown(lf,rg);
    	if( L <= mid ) modify( nd->ls, lf, mid, L, R, value );
    	if( mid < R ) modify( nd->rs, mid+1, rg, L, R, value );
    	nd->update();
    }
    void change( Node *nd, int lf, int rg, int L, int R, ll delta ){
    	if( L <= lf && rg <= R ){
    		nd->sum += (ll)(rg-lf+1)*delta;
    		if( nd->type == 0 ){
    			nd->type = 1;nd->delta = delta;
    		} else if( nd->type == 1 ) nd->delta += delta;
    		else if( nd->type == 2 ) nd->value += delta;
    		return ;
    	}
    	int mid = (lf+rg) >> 1;
    	nd->pushdown(lf,rg);
    	if( L <= mid ) change( nd->ls, lf, mid, L, R, delta );
    	if( mid < R ) change( nd->rs, mid+1, rg, L, R, delta );
    	nd->update();
    }
    ll query( Node *nd, int lf, int rg,int L, int R ){
    	if( L <= lf && rg <= R )
    		return nd->sum;
    	int mid = (lf+rg) >> 1;
    	nd->pushdown(lf,rg);
    	ll rt = 0;
    	if( mid >= L ) rt += query( nd->ls, lf, mid, L, R );
    	if( mid < R ) rt+= query( nd->rs, mid+1, rg, L, R );
    	return rt;
    }
    int main() {
    	freopen("setmod.in", "r", stdin);
    	freopen("setmod.out", "w", stdout);
    	scanf( "%d%d", &n, &m );
    	for( int i = 1; i <= n; i++ ) 
    		scanf( "%d", aa + i );
    	root = build( 1, n );
    	while( m-- ) {
    		char ss[10];
    		int l, r, delta, value;
    		scanf( "%s", ss );
    		if( ss[0] == 'c' ) {
    			l = read(); r = read(); delta = read();
    			change( root, 1, n, l, r, delta );
    		} else if( ss[0] == 'm' ) {
    			l = read(); r = read(); value = read();
    			modify( root, 1, n, l, r, value );
    		} else {
    			l = read(); r = read();
    			printf( "%I64d
    ", query( root, 1, n, l, r ) );
    		}
    	}
    	return 0;
    }
    

    例三 bzoj5039 序列维护

    JYY 现在有一个长度为 N 的序列 a1,a2,…,aN,有如下三种操作:
    1、 把数列中的一段数全部乘以一个值;
    2、 把数列中的一段数全部加上一个值;
    3、 询问序列中的一段数的和。
    从现在开始就是数组版了( k<<1 左儿子 k<<1|1右儿子 )
    我记得有一道树链剖分也是同样的操作
    这道题同样也是两个标记
    先处理乘法标记再处理加法标记
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 1000000 + 5;
    long long p,sum[N],f1[N],f2[N],a[100005];
    void pushdown( int k, int l, int r ){
    	int mid = l + r >> 1;
    	if( f1[k] == 0 && f2[k] == 1 ) return ;
    	f1[k<<1] = ( f1[k<<1] * f2[k] % p + f1[k] ) % p;
    	f2[k<<1] = f2[k<<1] * f2[k] % p;
    	sum[k<<1] = ( ( sum[k<<1] * f2[k] % p ) + ( f1[k] * (LL)(mid-l+1) ) % p ) % p;
    	f1[k<<1|1] = ( ( f1[k<<1|1] * f2[k] ) % p + f1[k] ) % p;
    	f2[k<<1|1] = f2[k<<1|1] * f2[k] % p;
    	sum[k<<1|1] = ( ( sum[k<<1|1] * f2[k] ) % p + ( f1[k] * (LL)(r-mid) ) % p) % p;
    	f1[k] = 0; f2[k] = 1;
    }
    void update( int k ){ sum[k] = ( sum[k<<1] + sum[k<<1|1] ) % p; }
    void modify( int k, int l, int r, int L, int R, int val ){
    	if( l >= L && r <= R ){
    		f1[k] += val; sum[k] += ( val * (LL)( r - l + 1 ) ) % p; sum[k] %= p; return ;
    	}
    	pushdown( k, l, r );
    	int mid = l + r >> 1;
    	if( L <= mid ) modify( k<<1, l, mid, L, R, val );
    	if( R > mid ) modify( k<<1|1, mid+1, r, L, R, val );
    	update( k );
    }
    void change( int k, int l, int r, int L, int R, int val ){
    	if( l >= L && r <= R ){
    		f2[k] *= val; f1[k] *= val; f2[k] %= p; f1[k] %= p; sum[k] *= val; sum[k] %= p; return ;
    	}
    	pushdown( k, l, r );
    	int mid = l + r >> 1;
    	if( L <= mid ) change( k<<1, l, mid, L, R, val );
    	if( R > mid ) change( k<<1|1, mid+1, r, L, R, val );
    	update( k );
    }
    long long query( int k, int l, int r, int L, int R ){
    	if( l >= L && r <= R ){
    		return sum[k]%p;
    	}
    	pushdown( k, l, r );
    	int mid = l + r >> 1; long long res = 0;
    	if( L <= mid ) res = (res+query( k<<1, l, mid, L, R ))%p;
    	if( R > mid ) res = (res+query( k<<1|1, mid+1, r, L, R ))%p;
    	update( k ); return res;
    }
    int n,q;
    void build( int k, int l, int r ){
    	f1[k] = 0; f2[k] = 1;
    	if( l == r ){
    		sum[k] = a[l]%p; return ;
    	}
    	int mid = l + r >> 1;
    	build( k<<1, l, mid );
    	build( k<<1|1, mid+1, r );
    	update( k );
    }
    int main(){
    	cin>>n>>p;
    	for( int i = 1; i <= n; i++ ) scanf("%d", &a[i]);
    	build( 1, 1, n );
    	scanf("%d", &q);
    	while( q-- ){
    		int f,t,g,c;
    		scanf("%d%d%d", &f, &t, &g);
    		if( f == 1 ){
    			scanf("%d", &c);
    			change( 1, 1, n, t, g, c );
    		}
    		if( f == 2 ){
    			scanf("%d", &c);
    			modify( 1, 1, n, t, g, c );
    		}
    		if( f == 3 ){
    			printf("%d
    ", query( 1, 1, n, t, g ));
    		}
    	}
    	return 0;
    }
    

     

    例四 bzoj1593Hotel 旅馆

    题意,01序列,每次操作清空区间,询问di,若有r满足r。。。r+di-1皆为0,选一个最小的填满这个区间,否则不填

    01线段树入门题

    需要维护的值是区间最大0段,右端开始最多0连续的数量,左端开始最多连续0的数量,这个没有难度

    这道题的询问可不要搞成区间问题了,询问的时候优先询问靠左

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 50000 + 5;
    int sum[N*4],lmx[N*4],rmx[N*4],siz[N*4],flag[N*4],n,m;
    void update( int k ){
    	sum[k] = max( max( sum[k<<1], sum[k<<1|1] ), lmx[k<<1|1]+rmx[k<<1] );
    	lmx[k] = lmx[k<<1];
    	if( sum[k<<1] == siz[k<<1] ) lmx[k] = sum[k<<1] + lmx[k<<1|1];
    	rmx[k] = rmx[k<<1|1];
    	if( sum[k<<1|1] == siz[k<<1|1] ) rmx[k] = sum[k<<1|1] + rmx[k<<1];
    }
    void pushdown( int k, int l, int  r ){
    	if( flag[k] == 1 ){
    		lmx[k<<1] = rmx[k<<1] = sum[k<<1] = siz[k<<1];
    		lmx[k<<1|1] = rmx[k<<1|1] = sum[k<<1|1] = siz[k<<1|1];
    		flag[k<<1] = flag[k<<1|1] = 1;
    	}
    	if( flag[k] == 0 ){
    		lmx[k<<1] = rmx[k<<1] = sum[k<<1] = 0;
    		lmx[k<<1|1] = rmx[k<<1|1] = sum[k<<1|1] = 0;
    		flag[k<<1] = flag[k<<1|1] = 0;
    	}
    	flag[k] = -1;
    }
    void build( int k, int l, int r ){
    	flag[k] = -1; lmx[k] = rmx[k] = sum[k] = siz[k] = r - l + 1;
    	if( l == r ){ return; }
    	int mid = l + r >> 1;
    	build( k<<1, l, mid );
    	build( k<<1|1, mid+1, r );
    	update( k );
    }
    void change( int k, int l, int r, int L, int R, int delta ){
    	if( l >= L && r <= R ){
    		lmx[k] = rmx[k] = sum[k] = siz[k] * delta;
    		flag[k] = delta;
    		return;
    	}
    	pushdown( k, l, r );
    	int mid = l + r >> 1;
    	if( mid >= L ) change( k<<1, l, mid, L, R, delta );
    	if( mid <  R ) change( k<<1|1, mid+1, r, L, R, delta );
    	update( k );
    }
    int query( int k, int l, int r, int x ){
    	pushdown( k, l, r );
    	int mid = l + r >> 1;
    	if( l == r ) return l;
    	if( sum[k<<1] >= x ) return query( k<<1, l, mid, x );
    	if( rmx[k<<1] + lmx[k<<1|1] >= x ) return mid - rmx[k<<1] + 1;
    	return query( k<<1|1, mid+1, r, x );
    }
    int main(){
    	scanf("%d%d", &n, &m);
    	build( 1, 1, n );
    	int opt, a, b;
    	while( m-- ){
    		scanf("%d%d", &opt, &a );
    		if( opt == 1 ){
    			if( sum[1] < a ){ puts("0"); continue; }
    			int l = query( 1, 1, n, a );
    			printf("%d
    ", l );
    			change( 1, 1, n, l, l+a-1, 0 );
    		}else{
    			scanf("%d", &b );
    			change( 1, 1, n, a, a+b-1, 1 );
    		}
    	}
    	return 0;
    }
    

    例五 bzoj4592 脑洞治疗仪

    这道题当时(6月份)时没有写出来,现在不想写了,等到noip完了填坑吧(可能没有机会了)

    例六 bzoj3211 花神游历各国

    区间开根区间求和

    我们发现每一个数开根号的次数比logn还要小

    于是维护区间是否为1/0,全是1/0我们就不会下去修改,否则暴力开根,时间复杂度在nlog2n内

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int N = 100000 + 5;
    int n,m,siz;
    int root,ls[N*4],rs[N*4]; bool flag[N*4];
    ll a[N],sum[N*4];
    void update( int k ){
    	sum[k] = sum[ls[k]]+sum[rs[k]];
    	flag[k] = flag[ls[k]]&&flag[rs[k]];
    }
    void build( int &nd, int l, int r ){
    	nd = ++siz;
    	if( l == r ){
    		sum[nd] = a[l];
    		if( sum[nd] == 0 || sum[nd] == 1 ) flag[nd] = 1;
    		return;
    	}
    	int mid = (l+r)>>1;
    	build( ls[nd], l, mid );
    	build( rs[nd], mid+1, r );
    	update(nd);
    }
    void modify( int nd, int l, int r, int L, int R ){
    	int mid = (l+r)>>1;
    	if( l == r ){
    		sum[nd] = (ll)sqrt(sum[nd]);
    		if( sum[nd] == 0 || sum[nd] == 1 ) flag[nd] = 1;
    		return;
    	}
    	if( L <= mid && !flag[ls[nd]] ) modify( ls[nd], l, mid, L, R );
    	if( R >  mid && !flag[rs[nd]] ) modify( rs[nd], mid+1, r, L, R );
    	update(nd);
    }
    ll query( int nd, int l, int r, int L, int R ){
    	int mid = (l+r)>>1; ll res=0;
    	if( l >= L && R >= r ) return sum[nd];
    	if( L <= mid ) res += query( ls[nd], l, mid, L, R );
    	if( R >  mid ) res += query( rs[nd], mid+1, r, L, R );
    	return res;
    }
    int main(){
    	scanf("%d", &n);
    	for( int i = 1; i <= n; i++ ) scanf("%d", &a[i]);
    	build( root, 1, n );
    	scanf("%d", &m);
    	for( int i = 1,opt,l,r; i <= m; i++ ){
    		scanf("%d%d%d", &opt, &l, &r);
    		if( opt == 1 ) printf("%lld
    ", query( root, 1, n, l, r ));
    		if( opt == 2 ) modify( root, 1, n, l, r );
    	}
    	return 0;
    }
    

    例七 tyvj1518

    这道题比较难

    #include<iostream>
    #include<cstdio>
    typedef long long ll;
    #define inf 1e18
    using namespace std;
    const int N = 150005;
    int n,m,a[N],ls[N*4],rs[N*4],id=0,root; char ch[5];
    ll mx[N*4],f1[N*4],f2[N*4],m_x[N*4],f_1[N*4],f_2[N*4];
    void update( int k ){
    	mx[k] = max(mx[ls[k]],mx[rs[k]]);
    	m_x[k] = max(m_x[ls[k]],m_x[rs[k]]);
    }
    void pushdown( int k ){
    	m_x[ls[k]] = max(m_x[ls[k]],max(f_2[k],mx[ls[k]]+f_1[k]));
    	if( f2[ls[k]] == -inf ) f_1[ls[k]] = max(f_1[ls[k]],f1[ls[k]]+f_1[k]);
    	else f_2[ls[k]] = max(f_2[ls[k]],f2[ls[k]]+f_1[k]);
    	if( f1[k] ){
    		if( f2[ls[k]] != -inf ) f2[ls[k]] += f1[k];
    		else f1[ls[k]] += f1[k];
    		mx[ls[k]] += f1[k];
    	}
    	if( f2[k] != -inf ){
    		mx[ls[k]] = f2[ls[k]] = f2[k];
    		f1[ls[k]] = 0;
    	}
    	f_1[ls[k]] = max(f_1[ls[k]],f1[ls[k]]);
    	f_2[ls[k]] = max(f_2[ls[k]],max(f2[k],f_2[k]));
    //======================================================================================================
    	m_x[rs[k]] = max(m_x[rs[k]],max(f_2[k],mx[rs[k]]+f_1[k]));
    	if( f2[rs[k]] == -inf ) f_1[rs[k]] = max(f_1[rs[k]],f1[rs[k]]+f_1[k]);
    	else f_2[rs[k]] = max(f_2[rs[k]],f2[rs[k]]+f_1[k]);
    	if( f1[k] ){
    		if( f2[rs[k]] != -inf ) f2[rs[k]] += f1[k];
    		else f1[rs[k]] += f1[k];
    		mx[rs[k]] += f1[k];
    	}
    	if( f2[k] != -inf ){
    		mx[rs[k]] = f2[rs[k]] = f2[k];
    		f1[rs[k]] = 0;
    	}
    	f_1[rs[k]] = max(f_1[rs[k]],f1[rs[k]]);
    	f_2[rs[k]] = max(f_2[rs[k]],max(f2[k],f_2[k]));
    //======================================================================================================
    	f_1[k] = f1[k] = 0;
    	f_2[k] = f2[k] = -inf;
    }
    void build( int &k, int l, int r ){
    	k = ++id; f1[k] = f_1[k] = 0; f2[k] = f_2[k] = -inf;
    	if( l == r ){
    		mx[k] = m_x[k] = (ll)a[l]; return;
    	}
    	int mid = (l+r)>>1;
    	build(ls[k],l,mid); build(rs[k],mid+1,r);
    	update(k);
    } 
    void change( int k, int l, int r, int L, int R, ll x, int type ){
    	if( l != r ) pushdown(k);
    	if( L <= l && r <= R ){
    		if( !type ) mx[k] += x, f1[k] += x, f_1[k] += x;
    		else f2[k] = f_2[k] = mx[k] = x;
    		m_x[k] = max(mx[k],m_x[k]);
    		return;
    	}
    	int mid = (l+r)>>1;
    	if( mid >= L ) change( ls[k], l, mid, L, R, x, type );
    	if( mid < R ) change( rs[k], mid+1, r, L, R, x, type );
    	update(k);
    }
    ll query( int k, int l, int r, int L, int R, int type ){
    	if( l != k ) pushdown(k);
    	if( L <= l && R >= r ){ return type ? m_x[k] : mx[k]; }
    	int mid = (l+r)>>1; ll res = -inf;
    	if( mid >= L ) res = max(res,query(ls[k],l,mid,L,R,type));
    	if( mid < R ) res = max(res,query(rs[k],mid+1,r,L,R,type));
    	return res;
    }
    int main(){
    	scanf("%d",&n);
    	for( int i = 1; i <= n; i++ ) scanf("%d", &a[i]);
    	build(root,1,n); scanf("%d", &m);
    	for( int i = 1,l,r,x; i <= m; i++ ){
    		scanf("%s", ch);
    		if( ch[0] == 'Q' ){ scanf("%d%d", &l, &r); printf("%lld
    ",query(root,1,n,l,r,0));}
    		if( ch[0] == 'A' ){ scanf("%d%d", &l, &r); printf("%lld
    ",query(root,1,n,l,r,1));}
    		if( ch[0] == 'P' ){ scanf("%d%d%d", &l, &r, &x); change(root,1,n,l,r,(ll)x,0);}
    		if( ch[0] == 'C' ){ scanf("%d%d%d", &l, &r, &x); change(root,1,n,l,r,(ll)x,1);}
    	}
    	return 0;
    }
    

    树上应用——维护dfs序

    顾名思义,dfs序是用我们dfs一棵树的顺序决定的,in表示开始访问这个点的时间,out是出这个点的时间,出去的时候不需要time++,这是鸡肋的

    所以( in[x], out[x] )这个区间就是x的子树,dfs序在处理树的子树问题时发挥着重要的作用

    例八 bzoj4551

    对一个点打标记

    询问某个节点打了标记的最近的祖先

    你对每一个点打标记相当于就是可能修改了这个点的子树的答案

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1000000 + 5;
    int last[N],cnt,ans,tot=0,n,q,root=1,in[N],out[N],dep[N];
    struct Edge{ int to,next; }e[N*2];
    void insert( int u, int v ){ e[++cnt].to = v; e[cnt].next = last[u]; last[u] = cnt; }
    void dfs( int x, int f ){
    	in[x] = ++tot; dep[x] = dep[f]+1;
    	for( int i = last[x]; i; i = e[i].next )
    		if( e[i].to ^ f )
    			dfs( e[i].to, x );
    	out[x] = tot;
    }
    int mx[N];
    void change( int k, int l, int r, int L, int R, int x ){
    	if( l >= L && r <= R ){
    		if( dep[mx[k]] < dep[x] ) mx[k] = x; return;
    	}
    	int mid = l + r >> 1;
    	if( L <= mid ) change( k<<1,   l,   mid, L, R, x );
    	if( R >  mid ) change( k<<1|1, mid+1, r, L, R, x );
    }
    void query( int k, int l, int r, int x ){
    	if( dep[ans] < dep[mx[k]] ) ans = mx[k];
    	if( l == r ) return ;
    	int mid = l + r >> 1;
    	if( x <= mid ) query( k<<1,   l,   mid, x );
    	if( x >  mid ) query( k<<1|1, mid+1, r, x );
    }
    int main(){
    	cin>>n>>q;
    	for( int i = 1,u,v; i < n; i++ ){
    		scanf("%d%d", &u, &v);
    		insert( u, v ); insert( v, u );
    	}
    	dep[1] = 1; dfs( 1, 0 );
    	for( int i = 1; i <= tot; i++ ) mx[i] = 1;
    //	build( root, 1, tot );
    	while( q-- ){
    		char opt[5]; int x;
    		scanf("%s", opt); scanf("%d", &x);
    		if( opt[0] == 'C' ) change( root, 1, n, in[x], out[x], x );
    		else{
    			ans = 0;
    			query( root, 1, n, in[x]);
    			printf("%d
    ", ans);
    		}
    	}
    	return 0;
    }
    

    例九 bzoj1782

    一棵树,有一些牛从根节点出发,到了之后下一头牛出发,求每头牛能遇到的牛的个数

    我们每到一个节点,相当于我们到它的子树答案++

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1000000 + 5;
    int last[N],cnt,ans,tot=0,n,q,root=1,in[N],out[N],dep[N],p[N],flag[N],sum[N];
    struct Edge{ int to,next; }e[N*2];
    void insert( int u, int v ){ e[++cnt].to = v; e[cnt].next = last[u]; last[u] = cnt; }
    void dfs( int x, int f ){
    	in[x] = ++tot; dep[x] = dep[f] + 1;
    	for( int i = last[x]; i; i = e[i].next )
    		if( e[i].to ^ f )
    			dfs( e[i].to, x );
    	out[x] = tot;
    }
    void pushdown( int k, int l, int r ){
    	if( flag[k] ){
    		int mid = l + r >> 1;
    		sum[k<<1] += flag[k] * ( mid - l + 1 ); flag[k<<1] += flag[k];
    		sum[k<<1|1] += flag[k] * ( r - mid ); flag[k<<1|1] += flag[k];
    		flag[k] = 0;
    	}
    }
    void update( int k ){
    	sum[k] = sum[k<<1] + sum[k<<1|1];
    }
    void modify( int k, int l, int r, int L, int R, int val ){
    	if( l >= L && r <= R ){
    		flag[k] += val; sum[k] += ( r - l + 1 ); return;
    	}
    	int mid = l + r >> 1;
    	pushdown( k, l, r );
    	if( L <= mid ) modify( k<<1, l, mid, L, R, val );
    	if( R >  mid ) modify( k<<1|1, mid+1, r, L, R, val );
    	update( k );
    }
    int query( int k, int l, int r, int L, int R ){
    	if( l >= L && R >= r ){
    		return sum[k];
    	}
    	pushdown( k, l, r );
    	int mid = l + r >> 1, res = 0;
    	if( L <= mid ) res += query( k<<1, l, mid, L, R );
    	if( R >  mid ) res += query( k<<1|1, mid+1, r, L, R );
    	return res;
    }
    int main(){
    	cin>>n;
    	for( int i = 1,u,v; i < n; i++ ){
    		scanf("%d%d", &u, &v);
    		insert( u, v ); insert( v, u );
    	}
    	for( int i = 1; i <= n; i++ ) scanf("%d", &p[i]);
    	dep[1] = 1; dfs( 1, 0 );
    	for( int i = 1; i <= n; i++ ){
    		int res = query( root, 1, n, in[p[i]], in[p[i]] );
    		modify( root, 1, n, in[p[i]], out[p[i]], 1 );
    		printf("%d
    ", res);
    	}
    	return 0;
    }
    

    例十 bzoj3306

    换根,子树最小,单点修改

    说起来简单实际神烦,考虑补集转化

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1000000 + 5;
    int last[N],cnt,q,ans,tot=0,n,root=1,in[N],out[N],dep[N],p[N],flag[N],sum[N],w[N],id,anc[N][20];
    struct Edge{ int to,next; }e[N*2];
    void insert( int u, int v ){ e[++cnt].to = v; e[cnt].next = last[u]; last[u] = cnt; }
    void dfs( int x, int f ){
    	in[x] = ++tot; dep[x] = dep[f] + 1; anc[x][0] = f;
    	for( int i = 1; i <= 19; i++ ) anc[x][i] = anc[anc[x][i-1]][i-1];
    	for( int i = last[x]; i; i = e[i].next )
    		if( e[i].to ^ f )
    			dfs( e[i].to, x );
    	out[x] = tot;
    }
    void pushdown( int k, int l, int r ){
    	if( flag[k] ){
    		sum[k<<1] = flag[k]; flag[k<<1] = flag[k];
    		sum[k<<1|1] = flag[k]; flag[k<<1|1] = flag[k];
    		flag[k] = 0;
    	}
    }
    void update( int k ){
    	sum[k] = min( sum[k<<1], sum[k<<1|1] );
    }
    void modify( int k, int l, int r, int L, int R, int val ){
    	if( l >= L && r <= R ){
    		flag[k] = val; sum[k] = val; return;
    	}
    	int mid = l + r >> 1;
    	pushdown( k, l, r );
    	if( L <= mid ) modify( k<<1, l, mid, L, R, val );
    	if( R >  mid ) modify( k<<1|1, mid+1, r, L, R, val );
    	update( k );
    }
    int query( int k, int l, int r, int L, int R ){
    	if( l >= L && R >= r ){
    		return sum[k];
    	}
    	pushdown( k, l, r );
    	int mid = l + r >> 1, res = 2147483647;
    	if( L <= mid ) res = min( res, query( k<<1, l, mid, L, R ) );
    	if( R >  mid ) res = min( res, query( k<<1|1, mid+1, r, L, R ) );
    	return res;
    }
    int up( int x, int p ){
    	for( int i = 19; i >= 0; i-- )
    		if( (1<<i) & p ) x = anc[x][i];
    	return x;
    }
    int main(){
    	cin>>n>>q;
    	for( int i = 1,u,v; i <= n; i++ ){
    		scanf("%d%d", &u, &v); if( u == 0 ) id = i;
    		else insert( u, i ), insert( i, u );
    		w[i] = v;
    	}
    	dep[id] = 1; dfs( id, 0 );
    	for( int i = 1; i <= n; i++ ) modify( root, 1, n, in[i], in[i], w[i] );
    	for( int i = 1; i <= q; i++ ){
    		char opt[5]; int x, y;
    		scanf("%s", opt);
    		if( opt[0] == 'V' ){
    			scanf("%d%d", &x, &y);
    			modify( root, 1, n, in[x], in[x], y );
    		}if( opt[0] == 'E' ){
    			scanf("%d", &id);
    		}if( opt[0] == 'Q' ){
    			scanf("%d", &x);
    			if( x == id ) printf("%d
    ", query( root, 1, n, 1, n ) );
    			else if( in[x] <= in[id] && out[id] <= out[x] ){
    				int y = up( id, dep[id]-dep[x]-1 );
    				printf("%d
    ", min( query( root, 1, n, 1, in[y]-1 ), query( root, 1, n, out[y]+1, n) ) );
    			}else printf("%d
    ", query( root, 1, n, in[x], out[x] ) );
    		}
    	}
    	return 0;
    }
    

    树上应用——树链剖分

    树链剖分这个东西好像好多都能用LCT

    可以和dfs序一起使用,注意要在树链剖分时计入度出度(这种题太烦了现在还没调出来)

    例十一 加帕里的聚会

    加帕里公园里有n个区域,n-1条道路将它们连接到了一起,形成了一个树的结构。开始时,第i个区域有Ai个friends,但是由于砂之星的作用,有时从x区域到y区域的简单路径上的所有区域的friends数量都会增加v,有时从x区域到y区域的简单路径上所有区域的friends数量都会变成v。

    有时,从x区域到y区域的简单路径上所有区域的friends想要聚会,聚会时需要从所有参加聚会的friends中选择一部分作为staff。加帕里的friends都很喜欢质数,因此她们希望staff和非staff的参与者的数量都是质数。

    请你告诉她们,每次聚会是否存在一种方案满足她们的要求

    裸的树链剖分,支持的操作如例二

    例十二 bzoj1984

    一棵树,支持链上修改,链上增减,u到v的最大边

    这道题修改操作与上一道题相同,但是这道题是边权,我们就要把边权下放到点权

    实际计算的时候仍然是当做点来做的,把一条边的权放到下面的点上面

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define inf 0x7fffffff
    const int N = 100000 + 5;
    using namespace std;
    int cnt=1,n,sz; char ch[10];
    int pos[N],bel[N],last[N],dep[N],siz[N],anc[N][20],id[N];
    int ls[N<<2],rs[N<<2],mx[N<<2],l[N<<2],r[N<<2],mf[N<<2],af[N<<2],root,idd;
    struct Edge{int to,next,v;}e[N<<1];
    void insert(int u,int v,int w){
    	e[++cnt].to = v; e[cnt].next = last[u]; e[cnt].v = w; last[u] = cnt;
    	e[++cnt].to = u; e[cnt].next = last[v]; e[cnt].v = w; last[v] = cnt;
    }
    void build( int &k, int L, int R ){
    	k = ++idd; l[idd] = L; r[idd] = R; mf[k] = -1;
    	if( L == R ) return; int mid = (L+R)>>1;
    	build( ls[k], L, mid ); build( rs[k], mid+1, R );
    }
    void update( int k ){
    	mx[k] = max(mx[ls[k]],mx[rs[k]]);
    }
    void pushdown( int k ){
    	if( l[k] == r[k] ) return;
    	if( mf[k] != -1 ){
    		af[ls[k]] = af[rs[k]] = 0;
    		mx[ls[k]] = mx[rs[k]] = mf[ls[k]] = mf[rs[k]] = mf[k];
    		mf[k] = -1;
    	} if( af[k] ){
    		mx[ls[k]] += af[k]; mx[rs[k]] += af[k];
    		if( mf[ls[k]] != -1 ) mf[ls[k]] += af[k]; else af[ls[k]] += af[k];
    		if( mf[rs[k]] != -1 ) mf[rs[k]] += af[k]; else af[rs[k]] += af[k];
    		af[k] = 0;
    	}
    }
    void change( int k, int x, int y, int v ){
    	pushdown(k);
    	if( x == l[k] && y == r[k] ){ mx[k] = mf[k] = v; return;}
    	int mid = (l[k]+r[k])>>1;
    	if( mid >= y ) change(ls[k],x,y,v);
    	else if( mid < x ) change(rs[k],x,y,v);
    	else {
    		change(ls[k],x,mid,v);
    		change(rs[k],mid+1,y,v);
    	}
    	update(k);
    }
    void modify( int k, int x, int y, int v ){
    	pushdown(k);
    	if( x == l[k] && y == r[k] ){ mx[k] += v; af[k] = v; return;}
    	int mid = (l[k]+r[k])>>1;
    	if( mid >= y ) modify(ls[k],x,y,v);
    	else if( mid < x ) modify(rs[k],x,y,v);
    	else {
    		modify(ls[k],x,mid,v);
    		modify(rs[k],mid+1,y,v);
    	}
    	update(k);
    }
    int query( int k, int x, int y ){
    	pushdown(k);
    	if( l[k] == x && r[k] == y ) return mx[k];
    	int mid = (l[k]+r[k])>>1;
    	if( mid >= y ) return query(ls[k],x,y);
    	else if( mid < x ) return query(rs[k],x,y);
    	else return max(query(ls[k],x,mid),query(rs[k],mid+1,y));
    }
    void solve_change( int x, int f, int v ){
    	while( bel[x] != bel[f] ){
    		change(root,pos[bel[x]],pos[x],v);
    		x = anc[bel[x]][0];
    	}
    	if( pos[f]+1 <= pos[x] ) change(root,pos[f]+1,pos[x],v);
    }
    void solve_modify( int x, int f, int v ){
    	while( bel[x] != bel[f] ){
    		modify(root,pos[bel[x]],pos[x],v);
    		x = anc[bel[x]][0];
    	}
    	if( pos[f]+1 <= pos[x] ) modify(root,pos[f]+1,pos[x],v);
    }
    int solve_query( int x, int f ){
    	int res = -inf;
    	while( bel[x] != bel[f] ){
    		res = max(res,query(root,pos[bel[x]],pos[x]));
    		x = anc[bel[x]][0];
    	}
    	if( pos[f]+1 <= pos[x] ) res = max(res,query(root,pos[f]+1,pos[x]));
    	return res;
    }
    void dfs1( int x, int f ){
    	siz[x] = 1;
    	for( int p = 1; p <= 16; p++ )
    		if( (1<<p) <= dep[x] ) anc[x][p] = anc[anc[x][p-1]][p-1];
    		else break;
    	for( int i = last[x]; i; i = e[i].next )
    		if( e[i].to != f ){
    			dep[e[i].to] = dep[x]+1;
    			anc[e[i].to][0] = x;
    			dfs1(e[i].to,x);
    			siz[x] += siz[e[i].to];
    		}
    }
    void dfs2( int x, int chain ){
    	int k = 0; bel[x] = chain; pos[x] = ++sz;
    	for( int i = last[x]; i; i = e[i].next )
    		if( dep[e[i].to] > dep[x] ){
    			if( siz[e[i].to] > siz[k] ) k = e[i].to;
    		} else{ id[i>>1] = x; modify(1,pos[x],pos[x],e[i].v); }
    	if( !k ) return; dfs2(k,chain);
    	for( int i = last[x]; i; i = e[i].next )
    		if( dep[e[i].to] > dep[x] && e[i].to != k ) dfs2(e[i].to,e[i].to);
    }
    int lca( int x, int y ){
    	if( dep[x] < dep[y] ) swap(x,y);
    	int t = dep[x]-dep[y];
    	for( int p = 0; p <= 16; p++ ) if( (1<<p)&t ) x = anc[x][p];
    	if( x == y ) return x;
    	for( int p = 16; p >= 0; p-- ) if( anc[x][p] != anc[y][p] ) x = anc[x][p], y = anc[y][p];
    	return anc[x][0];
    }
    int main(){
    	scanf("%d", &n);
    	for( int i = 1, u, v, w; i < n; i++ ) scanf("%d%d%d", &u, &v, &w), insert( u, v, w );
    	build( root, 1, n ); dfs1(1,0); dfs2(1,1);
    	while( 1 ){
    		int u,v,w,f;
    		scanf("%s", ch);
    		switch(ch[1]){
    			case 't':return 0;break;
    			case 'd':scanf("%d%d%d",&u,&v,&w);f=lca(u,v); solve_modify(u,f,w);solve_modify(v,f,w);break;
    			case 'o':scanf("%d%d%d",&u,&v,&w);f=lca(u,v); solve_change(u,f,w);solve_change(v,f,w);break;
    			case 'h':scanf("%d%d",&u,&w);change(1,pos[id[u]],pos[id[u]],w);break;
    			case 'a':scanf("%d%d",&u,&v);f=lca(u,v);printf("%d
    ",max(solve_query(u,f),solve_query(v,f)));break;
    		}
    	}
    }
    

    例十三 bzoj3694 最短路

    给出一个n个点m条边的无向图,n个点的编号从1~n,定义源点为1。定义最短路树如下:从源点1经过边集T到任意一点i有且仅有一条路径,且这条路径是整个图1到i的最短路径,边集T构成最短路树。 给出最短路树,求对于除了源点1外的每个点i,求最短路,要求不经过给出的最短路树上的1到i的路径的最后一条边。

    首先把最短路径树建出来,把不在这棵树上的边存下来,然后对答案进行更新
    具体是:
    开始都是inf的距离,对于一条边u->v,长度为w,设t=lca(u,v),对于t-v链上所有点x,可通过root -> t -> u -> v -> x 到达x,距离为dis[u]+w+dis[v]-dis[x],然后依次更新,查询就直接查单点就可以了
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define inf 0x7fffffff
    const int N = 100000 + 5;
    using namespace std;
    int cnt=1,n,m,sz;
    int pos[N],bel[N],last[N],dep[N],dis[N],siz[N],anc[N][20],u[N],v[N],w[N];
    int ls[N*4],rs[N*4],mx[N*4],sum[N*4],l[N*4],r[N*4],va[N*4],type[N*4],root,idd,cnt1=1;
    struct Edge{int to,next,v;}e[N<<1];
    void insert(int u,int v, int w ){
    	e[++cnt].to = v; e[cnt].next = last[u]; e[cnt].v = w; last[u] = cnt;
    	e[++cnt].to = u; e[cnt].next = last[v]; e[cnt].v = w; last[v] = cnt;
    }
    void dfs1( int x, int f ){
    	siz[x] = 1;
    	for( int p = 1; p <= 11; p++ )
    		if( (1<<p) <= dep[x] ) anc[x][p] = anc[anc[x][p-1]][p-1];
    		else break;
    	for( int i = last[x]; i; i = e[i].next )
    		if( e[i].to != f ){
    			dep[e[i].to] = dep[x]+1;
    			dis[e[i].to] = dis[x]+e[i].v;
    			anc[e[i].to][0] = x;
    			dfs1(e[i].to,x);
    			siz[x] += siz[e[i].to];
    		}
    }
    void dfs2( int x, int chain ){
    	int k = 0; bel[x] = chain; pos[x] = ++sz;
    	for( int i = last[x]; i; i = e[i].next )
    		if( dep[e[i].to] > dep[x] )
    			if( siz[e[i].to] > siz[k] ) k = e[i].to;
    	if( !k ) return; dfs2(k,chain);
    	for( int i = last[x]; i; i = e[i].next )
    		if( dep[e[i].to] > dep[x] && e[i].to != k ) dfs2(e[i].to,e[i].to);
    }
    int lca( int x, int y ){
    	if( dep[x] < dep[y] ) swap(x,y);
    	int t = dep[x]-dep[y];
    	for( int p = 0; p <= 11; p++ ) if( (1<<p)&t ) x = anc[x][p];
    	if( x == y ) return x;
    	for( int p = 11; p >= 0; p-- ) if( anc[x][p] != anc[y][p] ) x = anc[x][p], y = anc[y][p];
    	return anc[x][0];
    }
    void build( int &k, int L, int R ){
    	k = ++idd; l[k] = L; r[k] = R; type[k] = inf;
    	if( L == R ){ va[k] = inf; return; }
    	int mid = (L+R)>>1;
    	build( ls[k], L, mid ); build( rs[k], mid+1, R );
    }
    void pushdown( int k ){
    	if( l[k] == r[k] || type[k] == inf ) return;
    	type[ls[k]] = min(type[k],type[ls[k]]);
    	type[rs[k]] = min(type[k],type[rs[k]]);
    	if( l[ls[k]] == r[ls[k]] ) va[ls[k]] = min( va[ls[k]], type[k] );
    	if( l[rs[k]] == r[rs[k]] ) va[rs[k]] = min( va[rs[k]], type[k] );
    	type[k] = inf;
    }
    void modify( int k, int x, int y, int v ){
    	pushdown(k);
    	if( l[k] == x && r[k] == y ){
    		type[k] = min(type[k],v);
    		if( l[k] == r[k] ) va[k] = min(v,va[k]);
    		return;
    	}
    	int mid = (l[k]+r[k])>>1;
    	if( y <= mid ) modify( ls[k], x, y, v );
    	else if( x >  mid ) modify( rs[k], x, y, v );
    	else{ modify(ls[k],x,mid,v); modify(rs[k],mid+1,y,v); }
    }
    int query( int k, int x ){
    	pushdown(k);
    	if( l[k] == r[k] ) return va[k];
    	int mid = (l[k]+r[k])>>1;
    	if( x <= mid ) return query(ls[k],x);
    	else return query(rs[k],x);
    }
    void solve_modify( int x, int f, int v ){
    	while( bel[x] != bel[f] ){
    		modify( root, pos[bel[x]], pos[x], v );
    		x = anc[bel[x]][0];
    	}
    	if( x != f ) modify( root, pos[f]+1, pos[x], v );
    }
    int main(){
    	scanf("%d%d", &n, &m);
    	for( int i = 1,flag; i <= m; i++ ){
    		scanf("%d%d%d%d", &u[cnt1], &v[cnt1], &w[cnt1], &flag);
    		if( !flag ) cnt1++; else insert(u[cnt1],v[cnt1],w[cnt1]);
    	}
    	dfs1(1,0); dfs2(1,1); build( root, 1, n );
    	for( int i = 1; i < cnt1; i++ ){
    		int t = lca(u[i],v[i]);
    		solve_modify( u[i], t, dis[u[i]]+dis[v[i]]+w[i] );
    		solve_modify( v[i], t, dis[u[i]]+dis[v[i]]+w[i] );
    	}
    	for( int i = 2; i <= n; i++ ){
    		int d = query( root, pos[i] );
    		if( d != inf ) printf("%d ", d-dis[i]);
    	    else printf("-1 ");
    	}
    	return 0;
    }
    

    值域操作——值域线段树

    例十四 kth

    求区间第k大

    这是主席树的裸题,同时也可以用线段树套值域线段树实现,具体就是把一般线段树维护的是一段区间的值,而这里面一个区间相当于维护的是一棵值域线段树

    同样的道理,如果需要实现插入就用替罪羊树套值域线段树,见bzoj3065

    线段树其他应用

    例十五 area

    给出n 个矩形,求它们的面积并. 
    更准确一点,每个矩形将给出它的左上角和右下角的位置:x1; y1; x2; y2 
    这四个数都是整数且满足x1  x2; y1  y2. 
    我们需要你求: 
    area =j f(x; y) 2 Z  Z j 9 a rect: s:t: x1  x  x2 and y1  y  y2g j 

    扫描线裸题 题解 by idy002(比我的详细多了)

      扫描线,对于一个举行(x1,y1,x2,y2),将它看成两个事件:在x1 这个时间将(y1,y2) 这个区间加一,在x2+1 这个时间将(y1,y2) 这个区间减一。 
    这样,我们遍历整个时间,并在执行完这个时间的操作后看看有多少位置非0, 将其数量加到答案中,就完了,当然时间不能傻傻地一个一个枚,因为关键 
    的时间点最多2n 个,其它时候面积是没有变的,所以要一段一段地算。至于怎么用线段树实现那么查看有多少个非零的位置,需要注意对于任何一 
    个减一操作,前面一定有一个和它一样的加一操作,就只需要维护一下每个节点被完全覆盖的次数。再用另一个来统计子树中的那些修改导致这个节点还有 
    多少个非零。有点像标记永久化(我们讲的第二种区间修改的写法)。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 100000 + 10;
    typedef long long ll;
    inline int read() {
        int x = 0, f = 1;char ch = getchar();
        while( ch < '0' || ch > '9'){if(ch == '-')f = -1;ch = getchar();}
        while( ch >= '0' && ch <= '9' ){x = x*10+ch-'0';ch = getchar();}
        return x*f;
    }
    struct Event{
        int type;
        int time;
        int lf, rg;
        Event(){}
        Event( int type, int time, int lf, int rg )
            :type(type),time(time),lf(lf),rg(rg){}
    };
    bool operator<( const Event &r, const Event &s ) {
        return r.time < s.time;
    }
    struct Node{
        int cnt,sum;
        Node *ls, *rs;
        int query( int lf, int rg ){
            return cnt? rg - lf + 1 : sum;
        }
        void update( int lf, int rg ){
            int mid = (lf + rg) >> 1;
            sum = ls->query(lf,mid) + rs->query(mid+1,rg);
        }
    }pool[N*2], *tail = pool, *root;
    int n,total;
    Event events[N*2];
    Node *build( int lf, int rg ){
        Node *nd = ++tail;
        if( lf == rg ) nd->cnt = nd->sum = 0;
        else{
            int mid = (lf + rg) >> 1;
            nd->ls = build( lf, mid );
            nd->rs = build( mid+1, rg );
            nd->sum = nd->cnt = 0;
        }
        return nd;
    }
    void modify( Node *nd, int lf, int rg, int L, int R, int delta ) {
        if( L <= lf && R >= rg ){
            nd->cnt += delta;
            return;
        }
        int mid = (lf + rg) >> 1;
        if( L <= mid ) modify( nd->ls, lf, mid, L, R, delta );
        if( mid < R ) modify( nd->rs, mid+1, rg, L, R, delta );
        nd->update(lf,rg);
    }
    int main(){
        freopen("area.in","r",stdin);
        freopen("area.out","w",stdout);
        n = read(); int yu = n; int gu = 100000;
        for( int i = 1; i <= n; i++ ){
            int x1,x2,y1,y2;
            x1 = read(); y1 = read(); x2 = read(); y2 = read();
            events[total++] = Event( 1, x1, y1, y2 );
            events[total++] = Event( 2, x2+1, y1, y2 );
            if( y2 > yu ) yu = y2;
            if( y1 < gu ) gu = y1;
        }
        std::sort( events, events+total );
        root = build( gu, yu );
        ll ans = 0;
        for( int i = 0,j; i < total; i = j + 1 ){
            for( j = i; j + 1 < total && events[j+1].time == events[i].time; j++ );
            for( int k = i; k <= j; k++ )
                modify( root, gu, yu, events[k].lf, events[k].rg, events[k].type==1?1:-1);
            if( j+1 != total )
                ans += (ll)root->query(gu,yu)*(events[j+1].time - events[i].time);
        }
        printf("%I64d",ans);
        return 0;
    }
    

    例十六 [Usaco2013 Dec]Optimal Milking

    线段树实现dp,太水不想说了

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 40000 + 5; long long ans;
    int tl[N<<2],tr[N<<2],ta[N<<2],tn[N<<2],n,d,a[N];
    void update( int k ){
    	tl[k] = max( tl[k<<1] + tn[k<<1|1], max( ta[k<<1] + tn[k<<1|1], tl[k<<1] + tl[k<<1|1] ) );
    	tr[k] = max( tn[k<<1] + tr[k<<1|1], max( tn[k<<1] + ta[k<<1|1], tr[k<<1] + tr[k<<1|1] ) );
    	tn[k] = max( tn[k<<1] + tn[k<<1|1], max( tn[k<<1] + tl[k<<1|1], tr[k<<1] + tn[k<<1|1] ) );
    	ta[k] = max( ta[k<<1] + tr[k<<1|1], max( tl[k<<1] + tr[k<<1|1], tl[k<<1] + ta[k<<1|1] ) );
    }
    void build( int k, int l, int r ){
    	if( l == r ){
    		ta[k] = a[l]; return;
    	}
    	int mid = l + r >> 1;
    	build( k<<1, l, mid );
    	build( k<<1|1, mid+1, r );
    	update( k );
    }
    void change( int k, int l, int r, int x, int val ){
    	if( l == r ){ ta[k] = val; return; }
    	int mid = l + r >> 1;
    	if( x <= mid ) change( k<<1, l, mid, x, val );
    	if( x >  mid ) change( k<<1|1, mid+1, r, x, val );
    	update( k );
    }
    int main(){
    	scanf( "%d%d", &n, &d );
    	for( int i = 1; i <= n; i++ ) scanf( "%d", &a[i] );
    	build( 1, 1, n );
    	while( d-- ){
    		int i,m;
    		scanf( "%d%d", &i, &m );
    		change( 1, 1, n, i, m );
    		ans += max( max( tl[1], tr[1] ), max( tn[1], ta[1] ) );
    	}
    	printf( "%lld
    ", ans );
    	return 0;
    }
    
  • 相关阅读:
    docker-compose.yml容器网络
    docker-compose搭建redis集群
    docker-compose简易搭建rabbitmq
    docker仓库快速搭建
    CNN是怎样一步步工作的?
    全连接层分类的原理
    vector中erase()与insert()用法
    MySQL安装教程
    红黑树与AVL树比较
    C++内存管理(超长,例子很详细,排版很好)
  • 原文地址:https://www.cnblogs.com/youhavepeople/p/7701976.html
Copyright © 2011-2022 走看看