线段树 区间合并
区间合并入门题,照着代码打的,
题意:1 a:询问是不是有连续长度为a的空房间,有的话住进最左边
2 a b:将[a,a+b-1]的房间清空
思路:记录区间中最长的空房间,开三个数组,msum[rt]表示节点rt内连续的1的个数的最大值,lsum[rt]表示从节点rt左端点开始连续1的个数,rsum[rt]表示从节点rt右端点开始连续1的个数。。
线段树操作:update:区间替换 query:询问满足条件的最左端点
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 #include<string> 6 #include<cmath> 7 #include<map> 8 #include<queue> 9 10 using namespace std; 11 12 #define mnx 50005 13 #define Pi acos(-1.0) 14 #define ull unsigned long long 15 #define ll long long 16 #define inf 0x3f3f3f3f 17 #define eps 1e-8 18 #define MP make_pair 19 #define lson l, m, rt << 1 20 #define rson m+1, r, rt << 1 | 1 21 #define mod 2333333 22 23 int msum[mnx<<2], lsum[mnx<<2], rsum[mnx<<2], cover[mnx<<2]; 24 void pushup( int rt, int m ){ 25 lsum[rt] = lsum[rt<<1]; 26 rsum[rt] = rsum[rt<<1|1]; 27 if( lsum[rt] == m - ( m >> 1 ) ) lsum[rt] += lsum[rt<<1|1]; 28 if( rsum[rt] == ( m >> 1 ) ) rsum[rt] += rsum[rt<<1]; 29 msum[rt] = max( lsum[rt<<1|1] + rsum[rt<<1], max( msum[rt<<1], msum[rt<<1|1] ) ); 30 } 31 void pushdown( int rt, int m ){ 32 if( cover[rt] != -1 ){ 33 cover[rt<<1] = cover[rt<<1|1] = cover[rt]; 34 msum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = cover[rt] ? 0 : m - ( m >> 1 ); 35 msum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = cover[rt] ? 0 : m >> 1; 36 cover[rt] = -1; 37 } 38 } 39 void build( int l, int r, int rt ){ 40 msum[rt] = lsum[rt] = rsum[rt] = r - l + 1; 41 if( l == r ) return; 42 int m = ( l + r ) >> 1; 43 build( lson ); build( rson ); 44 } 45 void update( int L, int R, int v, int l, int r, int rt ){ 46 if( L <= l && R >= r ){ 47 msum[rt] = rsum[rt] = lsum[rt] = v ? 0 : r - l + 1; 48 cover[rt] = v; 49 return ; 50 } 51 pushdown( rt, r - l + 1 ); 52 int m = ( l + r ) >> 1; 53 if( L <= m ) update( L, R, v, lson ); 54 if( R > m ) update( L, R, v, rson ); 55 pushup( rt, r - l + 1 ); 56 } 57 int query( int v, int l, int r, int rt ){ 58 if( l == r ) return l; 59 pushdown( rt, r - l + 1 ); 60 int m = ( l + r ) >> 1; 61 if( msum[rt<<1] >= v ) return query( v, lson ); 62 else if( rsum[rt<<1] + lsum[rt<<1|1] >= v ) return m - rsum[rt<<1] + 1; 63 else return query( v, rson ); 64 } 65 int main(){ 66 int n, m; 67 while( scanf( "%d %d", &n, &m ) != EOF ){ 68 memset( cover, -1, sizeof(cover) ); 69 build( 1, n, 1 ); 70 int op, a, b; 71 while( m-- ){ 72 scanf( "%d", &op ); 73 if( op == 1 ){ 74 scanf( "%d", &a ); 75 if( msum[1] < a ){ 76 puts( "0" ); continue; 77 } 78 int ans = query( a, 1, n, 1 ); 79 printf( "%d\n", ans ); 80 update( ans, ans + a - 1, 1, 1, n, 1 ); 81 } 82 else{ 83 scanf( "%d %d", &a, &b ); 84 update( a, a + b - 1, 0, 1, n, 1 ); 85 } 86 } 87 } 88 return 0; 89 }
在yamiedie的指导下,过题总是那么轻松。。题目意思很简单,有n个村庄,一开始左右相连,D x表示这个村庄被破坏,就不能与左右两个村庄相连,R 表示修复最近一个被破坏的村庄,Q x表示询问和x村庄相连续的村庄有多少个。。
做法:线段树的区间连续问题。。D x 就是单点更新,跟上一题差不多。。Q x就先查询x有没有被破坏,没有被破坏就查询区间[1, x-1]的右端点的连续个数rsum[],再查询[x+1,n]的左端点的连续个数lsum[],注意 x - 1 > 0 和 x + 1 <= n的条件,不然会跪的。。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<string> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #include<vector> 9 10 using namespace std; 11 12 #define mnx 200050 13 #define mxn 50050 14 #define LL long long 15 #define mod 1000000007 16 #define inf 0x3f3f3f3f 17 #define eps 1e-10 18 #define Pi acos(-1.0) 19 #define lson l, m, rt << 1 20 #define rson m + 1, r, rt << 1 | 1 21 22 int lsum[mnx], rsum[mnx], msum[mnx], pre[mxn], n; 23 bool vis[mxn]; 24 void build( int l, int r, int rt ){ 25 lsum[rt] = rsum[rt] = msum[rt] = r - l + 1; 26 if( l == r ){ 27 return ; 28 } 29 int m = ( l + r ) >> 1; 30 build( lson ); 31 build( rson ); 32 } 33 void pushup( int rt, int m ){ 34 lsum[rt] = lsum[rt<<1]; 35 rsum[rt] = rsum[rt<<1|1]; 36 if( lsum[rt<<1] == m - ( m >> 1 ) ) lsum[rt] += lsum[rt<<1|1]; 37 if( rsum[rt<<1|1] == ( m >> 1 ) ) rsum[rt] += rsum[rt<<1]; 38 msum[rt] = max( lsum[rt<<1|1] + rsum[rt<<1], max( msum[rt<<1], msum[rt<<1|1] ) ); 39 } 40 void update( int ok, int k, int l, int r, int rt ){ 41 if( l == r ){ 42 msum[rt] = lsum[rt] = rsum[rt] = ok ? r - l + 1 : 0; 43 return ; 44 } 45 int m = ( l + r ) >> 1; 46 if( k <= m ) update( ok, k, lson ); 47 else update( ok, k, rson ); 48 pushup( rt, r - l + 1 ); 49 } 50 int query1( int L, int R, int l, int r, int rt ){ 51 if( L == l && R == r ){ 52 return rsum[rt]; 53 } 54 int m = ( l + r ) >> 1; 55 if( R <= m ) return query1( L, R, lson ); 56 if( L > m ) return query1( L, R, rson ); 57 int ret = query1( m + 1, R, rson ); 58 if( ret == R - m ){ 59 ret += query1( L, m, lson ); 60 } 61 return ret; 62 } 63 int query2( int L, int R, int l, int r, int rt ){ 64 if( L == l && R == r ){ 65 return lsum[rt]; 66 } 67 int m = ( l + r ) >> 1; 68 if( R <= m ) return query2( L, R, lson ); 69 if( L > m ) return query2( L, R, rson ); 70 int ret = query2( L, m, lson ); 71 if( ret == m - L + 1 ){ 72 ret += query2( m + 1, R, rson ); 73 } 74 return ret; 75 } 76 int main(){ 77 int m; 78 while( scanf( "%d %d", &n, &m ) != EOF ){ 79 build( 1, n, 1 ); 80 memset( vis, 0, sizeof(vis) ); 81 char ch[2]; int k, cnt = 0; 82 while( m-- ){ 83 scanf( "%s", ch ); 84 if( ch[0] == 'R' ){ 85 if( cnt == 0 ) continue; 86 k = pre[cnt--]; 87 if( vis[k] ) update( 1, k, 1, n, 1 ); 88 vis[k] = 0; 89 } 90 else if( ch[0] == 'D' ){ 91 scanf( "%d", &k ); 92 if( k < 1 || k > n ) continue; 93 pre[++cnt] = k; 94 if( !vis[k] ) update( 0, k, 1, n, 1 ); 95 vis[k] = 1; 96 } 97 else{ 98 scanf( "%d", &k ); 99 int ans = query1( k, k, 1, n, 1 ); 100 if( ans ){ 101 if( k > 1 ) ans += query1( 1, k-1, 1, n, 1 ); 102 if( k < n ) ans += query2( k+1, n, 1, n, 1 ); 103 } 104 printf( "%d\n", ans ); 105 } 106 } 107 } 108 return 0; 109 }
题意:有四个指令,New x就是申请一段长度为x的内存空间;Free x就是找一个包含X的区间,把那个区间释放掉;Get x就是从1到N 第x个区间;Reset 就是把所有的内存释放掉。
做法:线段树,int lsum[mnx], rsum[mnx], msum[mnx], sum[mnx], tot[mnx], L[mnx], R[mnx], cover[mnx], cnt。。先解释一下各个数组的含义,msum[rt]表示节点rt内连续的可用空间的长度的最大值,lsum[rt]表示从节点rt左端点开始连续可用空间的长度,rsum[rt]表示从节点rt右端点开始连续可用空间的长度;cover[rt] 表示这个区间有没有被用,-1就是最初没有使用的状态,1表示这个区间全都用了,0表示这个区间已经释放掉了;sum[rt]用来记录这是第cnt个被用的区间,sum[rt]=0表示这个区间没有被用,L[cnt]和R[cnt]表示第cnt个被用的区间的左端点和右端点是多少,这3个数组用来辅助Free操作;tot[]就是用来辅助Get操作,表示当前有多少个区间被用了,每用一个区间,就在该区间的左端点的tot[]上加上1,每释放一个区间,就在释放的区间的左端点减去1。。
对于Reset操作,不能重建线段树,这样会超时,只需要从 (1, n)成段更新,利用懒惰标记向下更新。。update函数就是用来更新 要用的区间或者要释放的区间,update1就是用来更新 左端点 tot[]的值,query函数就是在New x的时候,查找可用内存的左端点,find函数就是在Free x的时候查找 x的cnt值, findtot就是在Get x的时候查找 第x个被用的区间的左端点
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<string> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #include<vector> 9 10 using namespace std; 11 12 #define mnx 200050 13 #define mxn 50050 14 #define LL long long 15 #define mod 1000000007 16 #define inf 0x3f3f3f3f 17 #define eps 1e-10 18 #define Pi acos(-1.0) 19 #define lson l, m, rt << 1 20 #define rson m + 1, r, rt << 1 | 1 21 22 int lsum[mnx], rsum[mnx], msum[mnx], sum[mnx], tot[mnx], L[mnx], R[mnx], cover[mnx], cnt; 23 void build( int l, int r, int rt ){ 24 lsum[rt] = rsum[rt] = msum[rt] = r - l + 1; 25 cover[rt] = -1, sum[rt] = 0, tot[rt] = 0; 26 if( l == r ) return; 27 int m = ( l + r ) >> 1; 28 build( lson ); 29 build( rson ); 30 } 31 void pushup( int rt, int m ){ 32 tot[rt] = tot[rt<<1] + tot[rt<<1|1]; 33 lsum[rt] = lsum[rt<<1]; 34 rsum[rt] = rsum[rt<<1|1]; 35 if( lsum[rt<<1] == ( m - ( m >> 1 ) ) ) lsum[rt] += lsum[rt<<1|1]; 36 if( rsum[rt<<1|1] == ( m >> 1 ) ) rsum[rt] += rsum[rt<<1]; 37 msum[rt] = max( rsum[rt<<1] + lsum[rt<<1|1], max( msum[rt<<1], msum[rt<<1|1] ) ); 38 } 39 void pushdown( int rt, int m ){ 40 if( cover[rt] != -1 ){ 41 cover[rt<<1] = cover[rt<<1|1] = cover[rt]; 42 msum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = cover[rt] ? 0 : m - ( m >> 1 ); 43 msum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = cover[rt] ? 0 : ( m >> 1 ); 44 if( cover[rt] ){ 45 sum[rt<<1] = sum[rt<<1|1] = sum[rt]; 46 } 47 else sum[rt<<1] = sum[rt<<1|1] = 0; 48 if( tot[rt] == 0 ) tot[rt<<1] = tot[rt<<1|1] = tot[rt]; 49 cover[rt] = -1; 50 } 51 } 52 void update( int L, int R, int v, int l, int r, int rt ){ 53 if( L <= l && R >= r ){ 54 lsum[rt] = rsum[rt] = msum[rt] = v ? 0 : r - l + 1; 55 sum[rt] = v ? cnt : 0; cover[rt] = v; 56 return; 57 } 58 pushdown( rt, r - l + 1 ); 59 int m = ( l + r ) >> 1; 60 if( L <= m ) update( L, R, v, lson ); 61 if( R > m ) update( L, R, v, rson ); 62 pushup( rt, r -l + 1 ); 63 } 64 void update1( int k, int v, int l, int r, int rt ){ 65 if( l == r ){ 66 tot[rt] = v; 67 return ; 68 } 69 pushdown( rt, r - l + 1 ); 70 int m = ( l + r ) >> 1; 71 if( k <= m ) update1( k, v, lson ); 72 else update1( k, v, rson ); 73 pushup( rt, r - l + 1 ); 74 } 75 int query( int k, int l, int r, int rt ){ 76 if( l == r ) return l; 77 pushdown( rt, r - l + 1 ); 78 int m = ( l + r ) >> 1, ret; 79 if( msum[rt<<1] >= k ) ret = query( k, lson ); 80 else if( rsum[rt<<1] + lsum[rt<<1|1] >= k ){ 81 ret = m - rsum[rt<<1] + 1; 82 } 83 else ret = query( k, rson ); 84 pushup( rt, r -l + 1 ); 85 return ret; 86 } 87 int find( int k, int l, int r, int rt ){ 88 if( l == r ){ 89 return sum[rt]; 90 } 91 pushdown( rt, r - l + 1 ); 92 int m = ( l + r ) >> 1, ret; 93 if( k <= m ) ret = find( k, lson ); 94 else ret = find( k, rson ); 95 pushup( rt, r - l + 1 ); 96 return ret; 97 } 98 int findtot( int k, int l, int r, int rt ){ 99 if( l == r ){ 100 return l; 101 } 102 pushdown( rt, r - l + 1 ); 103 int m = ( l + r ) >> 1, ret; 104 if( tot[rt<<1] >= k ) ret = findtot( k, lson ); 105 else ret = findtot( k - tot[rt<<1], rson ); 106 pushup( rt, r -l + 1 ); 107 return ret; 108 } 109 int main(){ 110 int n, m, flag = 0; 111 //freopen( "in.txt", "w", stdout ); 112 while( scanf( "%d %d", &n, &m ) != EOF ){ 113 cnt = 0; 114 build( 1, n, 1 ); 115 char ch[50]; int k; 116 while( m-- ){ 117 scanf( "%s", ch ); 118 if( ch[0] == 'R' ){ 119 puts( "Reset Now" ); 120 update( 1, n, 0, 1, n, 1 ); 121 tot[1] = 0; 122 cnt = 0; 123 } 124 else if( ch[0] == 'N' ){ 125 scanf( "%d", &k ); 126 if( msum[1] < k || k <= 0 || k > n ){ 127 puts( "Reject New" );continue; 128 } 129 ++cnt; 130 int ans = query( k, 1, n, 1 ); 131 L[cnt] = ans, R[cnt] = ans + k - 1; 132 printf( "New at %d\n", ans ); 133 update( ans, ans + k - 1, 1, 1, n, 1 ); 134 update1( ans, 1, 1, n, 1 ); 135 } 136 else if( ch[0] == 'F' ){ 137 scanf( "%d", &k ); 138 int ans = find( k, 1, n, 1 ); 139 if( ans <= 0 || k <= 0 || k > n ){ 140 puts( "Reject Free" ); continue; 141 } 142 printf( "Free from %d to %d\n", L[ans], R[ans] ); 143 update( L[ans], R[ans], 0, 1, n, 1 ); 144 update1( L[ans], 0, 1, n, 1 ); 145 } 146 else{ 147 scanf( "%d", &k ); 148 if( tot[1] < k || k <= 0 ){ 149 puts( "Reject Get" ); continue; 150 } 151 int ans = findtot( k, 1, n, 1 ); 152 printf( "Get at %d\n", ans ); 153 } 154 } 155 cout<<endl; 156 } 157 return 0; 158 }