codeforce1679C. Rooks Defenders

传送门:线段树也不过如此


这标准线段树模板题,但是多年没碰线段树,现在已经基本不会写了T^T

首先是创建线段树。
在这里插入图片描述线段树是个满二叉树,如果从长度为n的数组上生成线段树,那么树节点个数m满足

m ≤ 2 q + 1 − 1 m \le 2^{q+1} - 1 m2q+11

其中 2 q > n ∧ 2 q − 1 < n 2^q > n \land 2^{q-1} < n 2q>n2q1<n
此外对于满二叉树来说,假设当前节点编号为n,则left node编号为 2 n 2n 2n,right node编号为 2 n + 1 2n+1 2n+1,ancestor编号为 n / 2 n/2 n/2。借助这些性质可以实现在数据中存储树节点时快速访问。
应用到这个题来说,需要建两棵线段树:一颗记录行,一颗记录列。根据题意,初始状态下没有rock,所以叶结点val都是0

然后是线段树更新:首先到叶结点,更新叶结点数据;然后,回溯的时候更新ancestor中记录的区间的值。
对于这个题来说,行线段树中非叶结点中记录的是从left行到right行中,一行最少有几个rock。回溯时根据left和right节点的值更新区间最小值。列线段树同理。

最后是查询。首先要明确的一点是,查询的区间 x x x始终是数组的子区间 x ⊂ [ 1 , n ] x\subset [1,n] x[1,n],所以查询的时候只考虑Ⅰ情况即可

在这里插入图片描述
具体到每个非叶结点所代表的区间查询时,借助中间变量mid来判断下一步需要查询的区间。设需要查询的区间为 [ l , r ] [l,r] [l,r],非叶结点代表区间 [ L , R ] [L,R] [L,R],非叶节点代表区间的区间中点为 m i d mid mid

在这里插入图片描述如果 m i d < l mid < l mid<l,说明需要去区间 [ m i d , R ] [mid, R] [mid,R]进一步查询,故go to right;
如果 r < m i d r < mid r<mid,说明需要去区间 [ L , m i d ] [L,mid] [L,mid]进一步查询, 故go to left;
如果 l < m i d ∧ m i d < r l<mid \land mid<r l<midmid<r,则说明需要在left 和 right两个子节点都进行查询。
如果 l ≤ L ∧ R ≤ r l\le L \land R\le r lLRr,也就是非叶节点代表区间x是待查询区间的子区间时,停止查询,返回该区间x的val。

其实线段树查询的时候,不断将查询区间细分,分为线段树中记录的区间,再见这些区间合并,最终得出结果。

在这里插入图片描述
具体到这个题,需要查询的是区间最小值,也就是所有子区间的最小值。

#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;

template<class T> void _deb(const char *name,T val){
    cout<<name<<val<<endl;
}

const int MAXN = 1e5+5;

struct tree{
    int left,right;
    int attacked;
};

int n,q;
tree row[MAXN*4],col[MAXN*4];
int creat(int pos,int l,int r, tree* T);
void update(int pos,bool, tree*);
int query(int l,int r,int pos, tree*);
int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    cin>>n>>q;
    creat(1,1,n, row);
    creat(1,1,n, col);
    while(q--) {
        int sym;
        cin>>sym;
        int commonA,commonB;
        switch(sym) {
            case 1:
                // insert
                cin>>commonA>>commonB;
                update(commonA,true,row);
                update(commonB,true, col);
                break;
            case 2:
                // remove
                cin>>commonA>>commonB;
                update(commonA,false,row);
                update(commonB,false,col);
                break;
            case 3:
                // query
                int x1,x2,y1,y2;
                cin>>x1>>y1>>x2>>y2;
                int ans = max(query(x1,x2,1,row), query(y1,y2,1,col));
                if(ans>0){
                    cout<<"YES"<<endl;
                }else{
                    cout<<"NO"<<endl;
                }
                break;
        }
    }

    re 0;
}
int creat(int pos,int l,int r, tree* T) {
    T[pos].left = l,T[pos].right = r;
    if(r == l) {
        return T[pos].attacked = 0;
    }

    int mid = l + (r-l)/2;
    int left_ans = creat(pos<<1,l,mid,T);
    int right_ans = creat(pos<<1|1, mid+1, r,T);
    return T[pos].attacked = 0;
}
void update(int pos,bool sta, tree* T){
    int inf = T[1].left;
    int sup = T[1].right;
    int nowPos = 1;
    while(inf<sup) {
        int mid = inf+(sup-inf)/2;
        if(pos>mid) {
            nowPos = nowPos<<1|1;
            inf = T[nowPos].left;
        }else{
            nowPos = nowPos<<1;
            sup = T[nowPos].right;
        }
    }

    T[nowPos].attacked = sta? T[nowPos].attacked+1 : T[nowPos].attacked-1;
    while(nowPos!=1) {
        nowPos = nowPos>>1;
        T[nowPos].attacked = min(T[nowPos<<1|1].attacked ,T[nowPos<<1].attacked);
    }
}
int query(int l,int r,int pos, tree* T){
    if(l<=T[pos].left && T[pos].right <= r){
        return T[pos].attacked;
    }
    int mid = T[pos].left + (T[pos].right - T[pos].left)/2;
    int ans = INT_MAX;
    if(r>mid){
        ans = min(ans, query(l,r,pos<<1|1, T));
    }
    if(l<=mid) {
        ans = min(ans,query(l,r,pos<<1, T));
    }
    return ans;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值