区间线段拆点、点位于区间、离线处理

LINK

给定有1e5个线段[l, r] (1 <= l <= r <= 1e9)
给定1e5个查询,对于每个查询K: 求所有包含K的线段里,最短的线段长度
{ [1, 4] [2, 4] [3, 5] [4, 5] }
K=4: 上面的所有的区间,都包含K。 最短的线段长度是[4,5] = 2

线段拆点

这个一个“线段拆点”的套路:
 比如有:interval { [1, 4]   [4, 5] }    Query = [ 3 , 4 ]
 我们拆成 interval.size() * 2 + Query.size() 个 “事件”
 所谓事件是:   {pos,  type,  msg}
 	pos: 坐标轴上的一维点。  {区间lef/rig点  或  query的点}
 	type:  0/1/2   表示{这个pos,是lef、rig、query}
 	msg:  如果是lef/rig点,存入该线段的长度; 否则,存入query的index
 	
		for(auto& v : A){
            events[M ++] = {v[0], 0, v[1] - v[0] + 1};
            events[M ++] = {v[1], 2, v[1] - v[0] + 1};
        }
        FOR(i, 0, Q.size() - 1, 1){
            events[M ++] = {Q[i], 1, i};
        }
  		sort(events, events + M, [](const auto& a, const auto& b) -> bool{
            if(a.pos != b.pos) return a.pos < b.pos;
            return a.type < b.type;
  ' 所以,type的顺序也很重要:[0是lef端点 < 1是query点 < 2是rig点] '
        });
        
  对所有event进行sort后, 用multiset来存 “当前”所有线段的长度:
    type=0:  将该线段的len(即msg),存入multiset里
    type=1:  '此时,multiset里所有的线段长度,其所指的线段 都是包含这个query点的!! '
    		  输出, multiset.begin()即可
    type=2:  从multiset里,将该线段 清除!!!

VE<int> ans(Q.size(), -1);
multiset<int> lens;
FOR(i, 0, M - 1, 1){
    int pos = events[i].pos, type = events[i].type, msg = events[i].msg;
    if(type == 0){
        lens.insert( msg );
    }else if(type == 1){
        if(lens.size()){
            ans[ msg ] = *lens.begin();
        }
    }else{
        lens.erase( lens.find( msg ) ); ' multiset的erase某个量,是会把所有元素都erase的!!! '
    }
}

线段树

暗存一个min_len[]数组, 然后遍历所有的线段, [l, r]
将: min_len[l, l+1, l+2, .., r] = min(self,  r-l+1);
最后,再遍历所有query  (即,区间修改  和  单点查询)
' 但: 1,线段长是1e9, 线段树是4e9 '
'  2,即使你把min_len[l,..,r]的过程,转换为: 对于query里的下标  '
'     此时, 就是一个1e5量级的问题。  '
'     但是,重点来了:   即便你对所有线段sort, min_len数组,并不是单调的!!! '
'    意味着: 你执行“区间min/max”的时间,并不是logN的!!!! '
'    这是重点!!!!    朴素的线段树 是处理不了的!!! '


TODO:  动态开点线段树          
上一篇:web前端入门到实战:几种HTML标签伪元素绑定事件的方式


下一篇:events.js:292 throw er; // Unhandled ‘error‘ event,vue项目打包失败