bzoj 3110
整体二分。
首先,第k大问题是满足二分性的(只要我们能够快速求出集合中比某个数小的数的个数,那么就可以快速找出该集合的第K大)。
然后考虑整体二分,关键是我们怎么将询问分到其对应的答案子区间中。
和普通的区间第K大的做法一样,我们先将修改按照大小排序(普通的区间第K大就是给出的原序列,而这里就是区间修改)。
设对于答案区间[lf,rg],有询问集合q,求出当前区间的mid,然后二分外面的修改数组,找出大小在[lf,mid]中的所有修改,将它们提出来,按照修改的顺序排序,然后和询问一起沿时间轴扫过去,过程中维护一个线段树,其以位置为下表,表示从最开始到现在每个位置中的数比mid小的数的个数。遇到修改就将对应的区间每个位置+1(区间修改),遇到询问就查询其对应区间的和(区间求和),根据其区间中小于等于mid的数的个数判断答案是与mid的关系,从而确定它的答案子区间。
时间复杂度:O( m * logn * logn )(改天把树套树的做法补上)。
1 /************************************************************** 2 Problem: 3110 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:5584 ms 7 Memory:6432 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <vector> 12 #include <algorithm> 13 #define N 50010 14 using namespace std; 15 16 struct Query { 17 int id, l, r, k; 18 int ans; 19 Query(){} 20 Query( int id, int l, int r, int k ):id(id),l(l),r(r),k(k){} 21 }; 22 struct Modify { 23 int id, l, r, k; 24 Modify(){} 25 Modify( int id, int l, int r, int k ):id(id),l(l),r(r),k(k){} 26 bool operator<( const Modify &o ) const { 27 return k<o.k; 28 } 29 }; 30 bool operator<( const Modify &o, int k ) { return o.k<k; } 31 bool operator<( int k, const Modify &o ) { return k<o.k; } 32 struct Node { 33 int s, tag; 34 Node *ls, *rs; 35 }pool[3*N], *tail=pool, *root; 36 37 int n, m, maxk; 38 int disc[N], dtot; 39 int ans[N]; 40 Query qry[N]; int tq; 41 Modify mdf[N]; int tm; 42 43 inline void update( Node *nd ) { 44 nd->s = nd->ls->s + nd->rs->s; 45 } 46 Node *build( int lf, int rg ) { 47 Node *nd = ++tail; 48 if( lf==rg ) return nd; 49 int mid=(lf+rg)>>1; 50 nd->ls = build( lf, mid ); 51 nd->rs = build( mid+1, rg ); 52 return nd; 53 } 54 void pushdown( Node *nd, int lf, int rg ) { 55 if( nd->tag ) { 56 int mid=(lf+rg)>>1; 57 nd->ls->s += (mid-lf+1)*nd->tag; 58 nd->rs->s += (rg-mid)*nd->tag; 59 nd->ls->tag += nd->tag; 60 nd->rs->tag += nd->tag; 61 nd->tag = 0; 62 } 63 } 64 void modify( Node *nd, int lf, int rg, int L, int R, int delta ) { 65 if( L<=lf && rg<=R ) { 66 nd->s += (rg-lf+1)*delta; 67 nd->tag += delta; 68 return; 69 } 70 pushdown(nd,lf,rg); 71 int mid=(lf+rg)>>1; 72 if( L<=mid ) modify( nd->ls, lf, mid, L, R, delta ); 73 if( R>mid ) modify( nd->rs, mid+1, rg, L, R, delta ); 74 update( nd ); 75 } 76 int query( Node *nd, int lf, int rg, int L, int R ) { 77 if( L<=lf && rg<=R ) return nd->s; 78 pushdown(nd,lf,rg); 79 int mid=(lf+rg)>>1; 80 int rt = 0; 81 if( L<=mid ) rt+=query( nd->ls, lf, mid, L, R ); 82 if( R>mid ) rt+=query( nd->rs, mid+1, rg, L, R ); 83 update(nd); 84 return rt; 85 } 86 bool cmp_id( int a, int b ) { 87 return mdf[a].id<mdf[b].id; 88 } 89 void binary( int lf, int rg, vector<int> vq ) { 90 if( vq.empty() ) return; 91 if( lf==rg ) { 92 for( int t=0; t<vq.size(); t++ ) 93 qry[vq[t]].ans = -disc[lf]; 94 return; 95 } 96 int mid=(lf+rg)>>1; 97 int lpos = lower_bound( mdf+1, mdf+1+tm, lf ) - mdf; 98 int rpos = upper_bound( mdf+1, mdf+1+tm, mid ) - mdf - 1; 99 vector<int> vm; 100 for( int i=lpos; i<=rpos; i++ ) 101 vm.push_back(i); 102 sort( vm.begin(), vm.end(), cmp_id ); 103 104 int i, j; 105 vector<int> ql, qr; 106 for( i=0,j=-1; i<vq.size(); i++ ) { 107 while( j+1<vm.size() && mdf[vm[j+1]].id<qry[vq[i]].id ) { 108 j++; 109 modify( root, 1, n, mdf[vm[j]].l, mdf[vm[j]].r, +1 ); 110 // fprintf( stderr, "modify( %d %d %d )\n", mdf[vm[j]].l, mdf[vm[j]].r, 111 // +1 ); 112 } 113 int c = query( root, 1, n, qry[vq[i]].l, qry[vq[i]].r ); 114 // fprintf( stderr, "query( %d %d ) = %d\n", qry[vq[i]].l, qry[vq[i]].r, c ); 115 if( qry[vq[i]].k<=c ) 116 ql.push_back( vq[i] ); 117 else { 118 qr.push_back( vq[i] ); 119 qry[vq[i]].k -= c; 120 } 121 } 122 for( int k=0; k<=j; k++ ) { 123 modify( root, 1, n, mdf[vm[k]].l, mdf[vm[k]].r, -1 ); 124 // fprintf( stderr, "modify( %d %d %d )\n", mdf[vm[k]].l, mdf[vm[k]].r, -1 ); 125 } 126 binary( lf, mid, ql ); 127 binary( mid+1, rg, qr ); 128 } 129 int main() { 130 scanf( "%d%d", &n, &m ); 131 for( int i=1,o,l,r,k; i<=m; i++ ) { 132 scanf( "%d%d%d%d", &o, &l, &r, &k ); 133 if( o==2 ) { 134 qry[++tq] = Query(i,l,r,k); 135 maxk = max( maxk, k ); 136 } else { 137 k = -k; 138 mdf[++tm] = Modify(i,l,r,k); 139 disc[++dtot] = k; 140 } 141 } 142 sort( disc+1, disc+1+dtot ); 143 dtot = unique( disc+1, disc+1+dtot ) - disc - 1; 144 145 sort( mdf+1, mdf+1+tm ); 146 for( int i=1; i<=tm; i++ ) 147 mdf[i].k = lower_bound( disc+1, disc+1+dtot, mdf[i].k ) - disc; 148 149 vector<int> vq; 150 for( int i=1; i<=tq; i++ ) 151 vq.push_back(i); 152 153 root = build( 1, n ); 154 binary( 1, dtot, vq ); 155 for( int i=1; i<=tq; i++ ) 156 printf( "%d\n", qry[i].ans ); 157 }
树状数组套线段树。
可以直接在树状数组上二分,可以少一个log的复杂度。
收获:
1、对树状数组的结构更清晰了,树状数组中的一个节点u,可以有四种操作:
1)跳到左边第一个不是其子树的节点:u-=lowbit(u)
2)跳到它的父亲节点:u+=lowbit(u)
3)跳到它的最左边的儿子:u-=lowbit(u)/2
4)跳到它右边大小是它一半的第一个节点:u+=lowbit(u)/2
我们常用的是前两种,而这道题如果用上后两种操作,就可以在树状数组上二分。(其实我们可以把树状数组看成是一棵二叉树,对于一个节点u,它的左儿子是3操作到达的 点,它的右儿子是4操作到达的点,奇数点是叶子)
2、“位置线段树?”
1 /************************************************************** 2 Problem: 3110 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:13392 ms 7 Memory:490776 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <algorithm> 12 #define N 50010 13 #define S 25000010 14 using namespace std; 15 16 typedef long long dnt; 17 18 struct Node { 19 dnt s; 20 int tag; 21 Node *ls, *rs; 22 }pool[S], *tail=pool, *null; 23 struct Proc { 24 int o, l, r; 25 dnt k; 26 Proc(){} 27 Proc( int o, int l, int r, dnt k ):o(o),l(l),r(r),k(k){} 28 }; 29 30 int n, m; 31 int disc[N], dtot; 32 Node *bit[65537]; int mbit=65536; 33 Node *cbit[65537]; 34 Proc prc[N]; 35 36 Node *newnode() { 37 Node *nd = ++tail; 38 nd->ls = nd->rs = null; 39 return nd; 40 } 41 void pushdown( Node *nd, int lf, int rg ) { 42 if( nd->tag ) { 43 if( nd->ls==null ) nd->ls = newnode(); 44 if( nd->rs==null ) nd->rs = newnode(); 45 int mid=(lf+rg)>>1; 46 nd->ls->tag += nd->tag; 47 nd->rs->tag += nd->tag; 48 nd->ls->s += (dnt) (mid-lf+1)*nd->tag; 49 nd->rs->s += (dnt) (rg-mid)*nd->tag; 50 nd->tag = 0; 51 } 52 } 53 void update( Node *nd ) { 54 nd->s = nd->ls->s + nd->rs->s; 55 } 56 void modify( Node *nd, int lf, int rg, int L, int R, int delta ) { 57 if( L<=lf && rg<=R ) { 58 nd->s += (dnt) (rg-lf+1)*delta; 59 nd->tag += delta; 60 return; 61 } 62 int mid=(lf+rg)>>1; 63 pushdown(nd,lf,rg); 64 if( L<=mid ) { 65 if( nd->ls==null ) nd->ls = newnode(); 66 modify( nd->ls, lf, mid, L, R, delta ); 67 } 68 if( R>mid ) { 69 if( nd->rs==null ) nd->rs = newnode(); 70 modify( nd->rs, mid+1, rg, L, R, delta ); 71 } 72 update( nd ); 73 } 74 dnt query( Node *nd, int lf, int rg, int L, int R ) { 75 if( nd==null ) return 0; 76 if( L<=lf && rg<=R ) return nd->s; 77 int mid=(lf+rg)>>1; 78 pushdown(nd,lf,rg); 79 dnt rt = 0; 80 if( L<=mid ) rt += query( nd->ls, lf, mid, L, R ); 81 if( R>mid ) rt += query( nd->rs, mid+1, rg, L, R ); 82 update( nd ); 83 return rt; 84 } 85 void init() { 86 null = ++tail; 87 null->s = null->tag = 0; 88 for( int i=1; i<=mbit; i++ ) { 89 bit[i] = newnode(); 90 cbit[i] = newnode(); 91 } 92 } 93 void modify( int l, int r, int k ) { 94 for( int i=k; i<=mbit; i+=i&-i ) { 95 // if( i<=3 ) fprintf( stderr, "modify( %d %d %d +1 )\n", i, l, r ); 96 modify( bit[i], 1, n, l, r, +1 ); 97 } 98 modify( cbit[k], 1, n, l, r, +1 ); 99 } 100 int query( int l, int r, dnt rk ) { 101 int u = mbit; 102 while((u&1)==0) { 103 int d = (u&-u)>>1; 104 dnt cc = query( bit[u], 1, n, l, r ); 105 dnt lc = cc-query( cbit[u], 1, n, l, r ); 106 // fprintf( stderr, "%d ls=%lld cc=%lld rk=%lld\n", u, lc, cc, rk ); 107 if( rk<=lc ) { 108 u -= d; 109 } else if( rk<=cc ) { 110 return u; 111 } else { 112 u += d; 113 rk -= cc; 114 } 115 } 116 return u; 117 } 118 int main() { 119 scanf( "%d%d", &n, &m ); 120 for( int i=1; i<=m; i++ ) { 121 int o, l, r; 122 dnt k; 123 scanf( "%d%d%d%lld", &o, &l, &r, &k ); 124 if( o==1 ) { 125 k = -k; 126 disc[++dtot] = k; 127 prc[i] = Proc( o, l, r, k ); 128 } else { 129 prc[i] = Proc( o, l, r, k ); 130 } 131 } 132 sort( disc+1, disc+1+dtot ); 133 dtot = unique( disc+1, disc+1+dtot ) - disc - 1; 134 init(); 135 for( int i=1; i<=m; i++ ) 136 if( prc[i].o==1 ) 137 prc[i].k=lower_bound(disc+1,disc+1+dtot,prc[i].k)-disc; 138 for( int i=1; i<=m; i++ ) { 139 if( prc[i].o==1 ) 140 modify( prc[i].l, prc[i].r, prc[i].k ); 141 else 142 printf( "%d\n", -disc[query(prc[i].l,prc[i].r,prc[i].k)] ); 143 } 144 } 145 146