返回顶部

2021“MINIEYE杯”中国大学生算法设计超级联赛(4)1008.Lawn of the Dead (线段树)

  • 题意:有一个\(n\)x\(m\)的矩阵,有\(k\)个炸弹分布在矩阵上,你最开始在\((1,1)\)的位置,每次可以向下或者向右走到空的单位,问你最多能到达多少单位(假设你可以从起点开始走无限次).

  • 题解:这题的数据范围很大,不好处理.假如某个位置\((x,y)\)有炸弹,并且\((x-1,y+1)\)\((x-1,y+1+k)\)这段位置全有炸弹,那么容易知道:\((x,y+1)\)\((x,y+1+k)\)是不能到达的,那么每次只考虑当前行和上一行的情况,用两个线段树维护连续区间和查询即可.

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int n,m,k;
    vector<int> col[N];
    struct misaka{
        int l,r;
        int cnt;
        int lazy;
    }tr[2][N<<4];
    
    void push_up(int id,int u){
        tr[id][u].cnt=tr[id][u<<1].cnt+tr[id][u<<1|1].cnt;
    }
    
    void push_down(int id,int u){
        if(tr[id][u].lazy==-1) return;
        tr[id][u<<1].cnt=(tr[id][u<<1].r-tr[id][u<<1].l+1)*tr[id][u].lazy;
        tr[id][u<<1|1].cnt=(tr[id][u<<1|1].r-tr[id][u<<1|1].l+1)*tr[id][u].lazy;
        tr[id][u<<1].lazy=tr[id][u<<1|1].lazy=tr[id][u].lazy;
        tr[id][u].lazy=-1;
    }
    
    void build(int id,int u,int l,int r){
        if(l==r){
            tr[id][u]={l,r,0,-1};
            return;
        }
        tr[id][u]={l,r,0,-1};
        int mid=(l+r)>>1;
        build(id,u<<1,l,mid);
        build(id,u<<1|1,mid+1,r);
        push_up(id,u);
    }
    
    void update(int id,int u,int l,int r,int v){   //1表示合法的单位,每次更新合法的点
        if(tr[id][u].l>=l && tr[id][u].r<=r){
            tr[id][u].cnt=(tr[id][u].r-tr[id][u].l+1)*v;
            tr[id][u].lazy=v;
            return;
        }
        push_down(id,u);
        int mid=(tr[id][u].l+tr[id][u].r)>>1;
        if(l<=mid) update(id,u<<1,l,r,v);
        if(r>mid) update(id,u<<1|1,l,r,v);
        push_up(id,u);
    }
    
    int query(int id,int u,int l,int r){ //询问[l,r]内连续的不合法的最远位置,即第一个1出现的位置
        if(!tr[id][u].cnt) return INF;
        if(tr[id][u].l==tr[id][u].r) return tr[id][u].l;
        int mid=(tr[id][u].l+tr[id][u].r)>>1;
        push_down(id,u);
        if(tr[id][u].l>=l && tr[id][u].r<=r){
            if(tr[id][u<<1].cnt) return query(id,u<<1,l,mid);
            else return query(id,u<<1|1,mid+1,r);
        }
        int res=INF;
        if(l<=mid) res=query(id,u<<1,l,r);
        if(r>mid) res=min(res,query(id,u<<1|1,l,r));
        return res;
    }
     
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int _;
        cin>>_;
        while(_--){
            cin>>n>>m>>k;
            rep(i,1,n) col[i].clear();
            rep(i,1,k){
                int x,y;
                cin>>x>>y;
                col[x].pb(y);
            }
            build(0,1,1,m);
            build(1,1,1,m);
            update(0,1,1,1,1);
            ll ans=0;
            rep(i,1,n){
                sort(col[i].begin(),col[i].end());
                int l=0;
                for(auto to:col[i]){
                    if(l+1<=to-1){
                        int pos=query((i&1)^1,1,l+1,to-1);
                        if(pos!=INF) update(i&1,1,pos,to-1,1);
                    }
                    l=to;
                }
                if(l+1<=m){
                    int pos=query((i&1)^1,1,l+1,m);
                    if(pos!=INF) update(i&1,1,pos,m,1);
                }
                ans+=tr[i&1][1].cnt;
                update((i&1)^1,1,1,m,0); //上一行全部改为不合法,当作下一行用
            }
            cout<<ans<<'\n';
        }
     
        return 0;
    }
    
    
posted @ 2021-08-04 13:37  Rayotaku  阅读(54)  评论(0编辑  收藏  举报