因为非常板,所以主要是代码
- Tyvj 1728 普通平衡树
- Chef and Sets
- [HNOI2012]永无乡
- Play with Chain
- [NOI2005]维修数列
题目很水,所以可能会出现代码部分细节出锅,但确实这些代码是能过得
还请多多包涵
Tyvj 1728 普通平衡树
luogu3369
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100005
struct node {int l, r, key, val, siz, cnt;
}t[maxn];
int Size, root;int Newnode( int x ) {t[++ Size].val = x;t[Size].key = rand();t[Size].siz = t[Size].cnt = 1;return Size;
}void pushup( int x ) {t[x].siz = t[t[x].l].siz + t[t[x].r].siz + t[x].cnt;
}void split( int now, int val, int &x, int &y ) {if( ! now ) x = y = 0;else {if( t[now].val <= val ) {x = now;split( t[now].r, val, t[now].r, y );pushup( x );}else {y = now;split( t[now].l, val, x, t[now].l );pushup( y );}}
}int merge( int x, int y ) {if( ! x or ! y ) return x + y;if( t[x].key < t[y].key ) {t[x].r = merge( t[x].r, y );pushup( x );return x;}else {t[y].l = merge( x, t[y].l );pushup( y );return y;}
}void add( int x ) {int l, r, L, R;split( root, x, l, r );split( l, x - 1, L, R );if( R ) {t[R].cnt ++;pushup( R );root = merge( merge( L, R ), r );}else {int t = Newnode( x );root = merge( merge( L, t ), r );}
}void del( int x ) {int l, r, L, R;split( root, x, l, r );split( l, x - 1, L, R );if( R and t[R].cnt > 1 ) {t[R].cnt --;pushup( R );root = merge( merge( L, R ), r );}else root = merge( L, r );
}void find_rank( int x ) {int l, r;split( root, x - 1, l, r );printf( "%d\n", t[l].siz + 1 );root = merge( l, r );
}void rank_find( int rt, int x ) {while( 1 ) {if( t[t[rt].l].siz >= x ) rt = t[rt].l;else if( t[t[rt].l].siz + t[rt].cnt >= x ) {printf( "%d\n", t[rt].val );return;}else x -= ( t[t[rt].l].siz + t[rt].cnt ), rt = t[rt].r;}
}void find_pre( int x ) {int l, r;split( root, x - 1, l, r );rank_find( l, t[l].siz );root = merge( l, r );
}void find_suf( int x ) {int l, r;split( root, x, l, r );rank_find( r, 1 );root = merge( l, r );
}int main() {int n;scanf( "%d", &n );while( n -- ) {int opt, x;scanf( "%d %d", &opt, &x );switch( opt ) {case 1 : { add( x ); break; }case 2 : { del( x ); break; }case 3 : { find_rank( x ); break; }case 4 : { rank_find( root, x ); break; }case 5 : { find_pre( x ); break; }case 6 : { find_suf( x ); break; }}}return 0;
}
Chef and Sets
codechef
每次暴力的合并两个集合
为了维护权值的小根堆性质,只能从集合中一个一个的并到另一个集合里面去
具体而言
- 每次操作集合个数较小的其中一个数
- 然后根据这个数的权值,分裂较大集合
- 把这个数并到大集合中,从小集合中删除
- 重复这样的操作,直到小集合为空
看似暴力,但是一共就nnn个数的集合并,没有新增的数
最坏就是nlognn\log nnlogn的(每次就个数相同的两个集合合并)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 300005
struct node { int siz, l, r, val, key; }t[maxn];
int root[maxn];void pushup( int x ) {t[x].siz = t[t[x].l].siz + t[t[x].r].siz + 1;
}void split_val( int now, int val, int &x, int &y ) {if( ! now ) x = y = 0;else {if( t[now].val <= val ) {x = now;split_val( t[now].r, val, t[now].r, y );pushup( x );}else {y = now;split_val( t[now].l, val, x, t[now].l );pushup( y );}}
}void split_id( int now, int cnt, int &x, int &y ) {if( ! now ) x = y = 0;else {if( t[t[now].l].siz + 1 <= cnt ) {x = now;split_id( t[now].r, cnt - ( t[t[now].l].siz + 1 ), t[now].r, y );pushup( x );}else {y = now;split_id( t[now].l, cnt, x, t[now].l );pushup( y );}}
}int merge( int x, int y ) {if( ! x or ! y ) return x + y;if( t[x].key < t[y].key ) {t[x].r = merge( t[x].r, y );pushup( x );return x;}else {t[y].l = merge( x, t[y].l );pushup( y );return y;}
}void find_kth( int now, int x ) {while( 1 ) {if( t[t[now].l].siz >= x ) now = t[now].l;else if( t[t[now].l].siz + 1 == x ) {printf( "%d\n", t[now].val );return;}else x -= ( t[t[now].l].siz + 1 ), now = t[now].r;}
}int main() {int n, Q;scanf( "%d %d", &n, &Q );for( int i = 1;i <= n;i ++ )t[i].siz = 1, t[i].val = root[i] = i, t[i].key = rand();char opt[10]; int a, b, k;while( Q -- ) {scanf( "%s %d", opt, &a );if( opt[0] == 'U' ) {scanf( "%d", &b );++ n;if( t[root[a]].siz < t[root[b]].siz )swap( a, b );root[n] = root[a];int l, r, L, R;while( root[b] ) {split_id( root[b], 1, l, r );split_val( root[n], t[l].val, L, R );root[n] = merge( merge( L, l ), R );root[b] = r;}}else {scanf( "%d", &k );find_kth( root[a], k );}}return 0;
}
[HNOI2012]永无乡
luogu3224
与上一题几乎是一模一样
不过刁钻了一下,给的是集合中某一个点,并不一定直接是集合的根
其实也米有什么变化
套一个并查集记录每个点所在集合的根就行了
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 500005
struct node {int l, r, id, val, key, siz;
}t[maxn];
int root[maxn], p[maxn], f[maxn];int find( int x ) {return x == f[x] ? x : f[x] = find( f[x] );
}void pushup( int x ) {t[x].siz = t[t[x].l].siz + t[t[x].r].siz + 1;
}void split_val( int now, int val, int &x, int &y ) {if( ! now ) x = y = 0;else {if( t[now].val <= val ) {x = now;split_val( t[now].r, val, t[now].r, y );pushup( x );}else {y = now;split_val( t[now].l, val, x, t[now].l );pushup( y );}}
}void split_id( int now, int cnt, int &x, int &y ) {if( ! now ) x = y = 0;else {if( t[t[now].l].siz + 1 <= cnt ) {x = now;split_id( t[now].r, cnt - ( t[t[now].l].siz + 1 ), t[now].r, y );pushup( x );}else {y = now;split_id( t[now].l, cnt, x, t[now].l );pushup( y ); }}
}int merge( int x, int y ) {if( ! x or ! y ) return x + y;if( t[x].key < t[y].key ) {t[x].r = merge( t[x].r, y );pushup( x );return x; }else {t[y].l = merge( x, t[y].l );pushup( y );return y;}
}void find_kth( int rt, int x ) {while( 1 ) {if( t[t[rt].l].siz >= x ) rt = t[rt].l;else if( t[t[rt].l].siz + 1 == x ) {printf( "%d\n", t[rt].id );return;}else x -= ( t[t[rt].l].siz + 1 ), rt = t[rt].r;}
}int main() {int n, m, Q, x, y, k;char opt[10];scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ )scanf( "%d", &p[i] );for( int i = 1;i <= n;i ++ ) {t[i].val = p[i];t[i].key = rand();t[i].siz = 1;t[i].id = i;root[i] = i;f[i] = i;}for( int i = 1;i <= m;i ++ ) {scanf( "%d %d", &x, &y );if( find( x ) == find( y ) ) continue;x = find( x );y = find( y );if( t[root[x]].siz < t[root[y]].siz ) swap( x, y );root[++ n] = root[x];f[n] = n;while( root[y] ) {int l, r, L, R;split_id( root[y], 1, l, r );split_val( root[n], t[l].val, L, R );root[n] = merge( merge( L, l ), R );root[y] = r;}f[find( x )] = f[find( y )] = n;}scanf( "%d", &Q );while( Q -- ) {scanf( "%s %d", opt, &x );if( opt[0] == 'Q' ) {scanf( "%d", &k );x = find( x );if( t[root[x]].siz < k ) printf( "-1\n" );else find_kth( root[x], k );}else {scanf( "%d", &y );if( find( x ) == find( y ) ) continue;x = find( x );y = find( y );if( t[root[x]].siz < t[root[y]].siz ) swap( x, y );root[++ n] = root[x];f[n] = n;while( root[y] ) {int l, r, L, R;split_id( root[y], 1, l, r );split_val( root[n], t[l].val, L, R );root[n] = merge( merge( L, l ), R );root[y] = r;}f[find( x )] = f[find( y )] = n;}}return 0;
}
Play with Chain
HDU3487
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 300005
vector < int > ans;
struct node {int l, r, val, key, siz, tag;
}t[maxn];void pushup( int x ) {t[x].siz = t[t[x].l].siz + t[t[x].r].siz + 1;
}void pushdown( int x ) {if( ! t[x].tag ) return;swap( t[x].l, t[x].r );t[t[x].l].tag ^= 1;t[t[x].r].tag ^= 1;t[x].tag = 0;
}void split( int now, int cnt, int &x, int &y ) {if( ! now ) x = y = 0;else {pushdown( now );if( t[t[now].l].siz + 1 <= cnt ) {x = now;split( t[now].r, cnt - ( t[t[now].l].siz + 1 ), t[now].r, y );pushup( x );}else {y = now;split( t[now].l, cnt, x, t[now].l );pushup( y );}}
}int merge( int x, int y ) {if( ! x or ! y ) return x + y;if( t[x].key < t[y].key ) {pushdown( x );t[x].r = merge( t[x].r, y );pushup( x );return x;}else {pushdown( y );t[y].l = merge( x, t[y].l );pushup( y );return y;}
}void print( int x ) {if( ! x ) return;pushdown( x );print( t[x].l );ans.push_back( t[x].val );print( t[x].r );
}int main() {int n, m, a, b, c; char opt[10];while( scanf( "%d %d", &n, &m ) ) {if( n == -1 and m == -1 ) return 0;for( int i = 1;i <= n;i ++ ) {t[i].l = t[i].r = t[i].tag = 0;t[i].siz = 1;t[i].val = i;t[i].key = rand();}int rt = 0;for( int i = 1;i <= n;i ++ )rt = merge( rt, i );int l, r, L, R;while( m -- ) {scanf( "%s %d %d", opt, &a, &b );if( opt[0] == 'C' ) {scanf( "%d", &c );split( rt, a - 1, l, r );split( r, b - a + 1, L, R );rt = merge( l, R );split( rt, c, l, r );rt = merge( merge( l, L ), r );}else {split( rt, a - 1, l, r );split( r, b - a + 1, L, R );t[L].tag ^= 1;rt = merge( l, merge( L, R ) );}}print( rt );printf( "%d", ans[0] );for( int i = 1;i < ans.size();i ++ )printf( " %d", ans[i] );printf( "\n" );ans.clear();}return 0;
}
[NOI2005]维修数列
BZOJ1500
把线段树的所有计算方法全都搬到平衡树上就行
#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 500005
#define lson t[now].l
#define rson t[now].r
#define inf 0x3f3f3f3f
queue < int > q;
struct node {int l, r, siz, val, key, reverse, cover, sum, max_sum, max_l, max_r;
}t[maxn];
int Size;int MakeNode( int x ) {int now;if( ! q.empty() ) now = q.front(), q.pop();else now = ++ Size;lson = 0; //左儿子 rson = 0; //右儿子 t[now].reverse = 0; //翻转标记 t[now].cover = inf; //覆盖标记 t[now].val = x; //权值 t[now].sum = x; //区间和 t[now].max_l = x; //从区间左端点开始连续最大子段和 t[now].max_r = x; //从区间右端点开始连续最大子段和 t[now].max_sum = x; //区间最大子段和 t[now].key = rand(); //修正值/键值t[now].siz = 1; //子树大小 return now;
}void pushup( int now ) {if( ! now ) return;t[now].siz = t[lson].siz + t[rson].siz + 1;t[now].sum = t[lson].sum + t[rson].sum + t[now].val;t[now].max_sum = max( max( t[lson].max_sum, t[rson].max_sum ), max( 0, t[lson].max_r ) + t[now].val + max( 0, t[rson].max_l ) );t[now].max_l = max( t[lson].max_l, t[lson].sum + t[now].val + max( 0, t[rson].max_l ) );t[now].max_r = max( t[rson].max_r, t[rson].sum + t[now].val + max( 0, t[lson].max_r ) );
}void modify( int now, int val ) {t[now].val = val;t[now].cover = val;t[now].sum = t[now].siz * val;t[now].max_l = max( 0, t[now].sum );t[now].max_r = max( 0, t[now].sum );t[now].max_sum = max( val, t[now].sum );
}void operation( int now ) {swap( lson, rson );swap( t[now].max_l, t[now].max_r );t[now].reverse ^= 1;
}void pushdown( int now ) {if( t[now].reverse ) {if( lson ) operation( lson );if( rson ) operation( rson );t[now].reverse = 0;}if( t[now].cover ^ inf ) {if( lson ) modify( lson, t[now].cover );if( rson ) modify( rson, t[now].cover );t[now].cover = inf;}
}void split( int now, int cnt, int &x, int &y ) {if( ! now ) x = y = 0;else {pushdown( now );if( t[lson].siz + 1 <= cnt) {x = now;split( rson, cnt - ( t[lson].siz + 1 ), rson, y );pushup( x );}else {y = now;split( lson, cnt, x, lson );pushup( y );}}
}int merge( int x, int y ) {if( ! x or ! y ) return x + y;pushdown( x );pushdown( y );if( t[x].key < t[y].key ) {t[x].r = merge( t[x].r, y );pushup( x );return x;}else {t[y].l = merge( x, t[y].l );pushup( y );return y;}
}void inque( int now ) {if( ! now ) return;q.push( now );inque( lson );inque( rson );
}int n, m, rt, l, r, L, R, k, pos, x;
char opt[10];
int main() {scanf( "%d %d", &n, &m );t[0].val = t[0].max_sum = -inf;for( int i = 1;i <= n;i ++ ) {scanf( "%d", &x );rt = merge( rt, MakeNode( x ) );}while( m -- ) {scanf( "%s", opt );switch( opt[2] ) {case 'S' : { //INSERTscanf( "%d %d", &pos, &k );split( rt, pos, l, r );for( int i = 1;i <= k;i ++ ) {scanf( "%d", &x );l = merge( l, MakeNode( x ) );}rt = merge( l, r );break;}case 'L' : { //DELETEscanf( "%d %d", &pos, &k );split( rt, pos - 1, l, r );split( r, k, L, R );rt = merge( l, R );inque( L );break;}case 'K' : { //MAKE-SAMEscanf( "%d %d %d", &pos, &k, &x );split( rt, pos - 1, l, r );split( r, k, L, R );modify( L, x );rt = merge( l, merge( L, R ) );break;}case 'V' : { //REVERSEscanf( "%d %d", &pos, &k );split( rt, pos - 1, l, r );split( r, k, L, R );operation( L );rt = merge( l, merge( L, R ) );break;}case 'T' : { //GET_SUMscanf( "%d %d", &pos, &k );split( rt, pos - 1, l, r );split( r, k, L, R );printf( "%d\n", t[L].sum );rt = merge( l, merge( L, R ) );break;}case 'X' : { //MAX-SUM;printf( "%d\n", t[rt].max_sum );break;}}}return 0;
}
只需要相信:只要多pushdown
就不会出现标记与点不对应的情况,那么这与线段树有什么区别呢??