hdu6992 Lawn of the Dead 查询区间会变的线段树小变形

原题链接
题意:
n*m的棋盘,k个地雷,从(1,1)开始走,只能走右下两个方向,显然地雷不能走,问能到达的格子数

思路:
两棵线段树维护一下上一次层能到的区间,然后根据这一层的地雷位置推出理论上能到的区间,再更新这一层实际能到的区间。

#include<bits/stdc++.h>
#define LL long long
#define INF INT64_MAX
#define MOD 1000000007
#define stree SegTree[f][root]
#define lson SegTree[f][root << 1]
#define rson SegTree[f][root << 1 | 1]
using namespace std;
typedef pair<int,int>pa;
const int N = 100005;
int SegTree[2][N*4], lz[2][N*4];
vector<int> v[N];
void Pushup(int root, int f){stree = lson+rson;}
void Pushdown(int root, int l, int r, int mid, int f)
{
    if(lz[f][root]==-1) return;
    lson = lz[f][root]*(mid-l+1);
    rson = lz[f][root]*(r-mid);
    lz[f][root<<1]=lz[f][root<<1|1]=lz[f][root];
    lz[f][root]=-1;
}
void update(int root, int l, int r, int ul, int ur, int val, int f)
{
    //printf("up l = %d, r = %d, root = %d\n", l, r, root);
    if(l>=ul && r<=ur)
    {
        //printf("root = %d\n", root);
        stree = (r-l+1)*val;
        lz[f][root] = val;
        return;
    }
    int mid = l+r>>1;
    Pushdown(root, l, r, mid, f);
    if(mid >= ul) update(root<<1, l, mid, ul, ur, val, f);
    if(mid < ur) update(root<<1|1, mid+1, r, ul, ur, val, f);
    Pushup(root, f);
    //printf("up %d tr= %d\n",root, stree);
}
int query(int root, int l, int r, int ql, int qr, int f)
{
    //printf("l = %d, r = %d, root = %d, stree = %d\n", l, r, root, stree);
    if(!stree)
    {
        //printf("int root = %d, stree = %d\n", root, stree);
        return INT_MAX;
    }
    if(l==r) return l;
    int mid = l+r>>1;
    Pushdown(root, l, r, mid, f);
    if(l>=ql && r<=qr)
    {
        if(lson>0) return query(root<<1, l, mid, l, mid, f);
        else return query(root<<1|1, mid+1, r, mid+1, r, f);
    }
    else
    {
        if(mid < ql) return query(root<<1|1, mid+1, r, ql, qr, f);
        else if(mid >= qr) return query(root<<1, l, mid, ql, qr, f);
        else return min(query(root<<1, l, mid, ql, mid, f), query(root<<1|1, mid+1, r, mid+1, qr, f));
    }
}
int main()
{
    int t, n, m, k, x, y;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 1;i <= n;i++) v[i].clear();
        for(int i = 1;i <= k;i++)
        {
            scanf("%d%d", &x, &y);
            v[x].push_back(y);
        }
        LL ans = 0;
        for(int i = 1;i <= m*4;i++) SegTree[0][i]=SegTree[1][i]=0,lz[0][i]=lz[1][i]=-1;
        update(1, 1, m, 1, 1, 1, 0);
        for(int i = 1;i <= n;i++)
        {
            int l = 0;
            sort(v[i].begin(), v[i].end());
            for(auto it: v[i])
            {
                if(it-1>=l+1)
                {
                    int pos = query(1, 1, m, l+1, it-1, i&1^1);
                    //printf("pos = %d, it = %d\n", pos, it);
                    if(pos!=INT_MAX) update(1, 1, m, pos, it-1, 1, i&1);
                }
                l = it;
            }
            if(l+1 <= m)
            {
                int pos = query(1, 1, m, l+1, m, i&1^1);
                //printf("2           pos = %d, it = %d\n", pos, m);
                if(pos!=INT_MAX) update(1, 1, m, pos, m, 1, i&1);
            }
            ans += SegTree[i&1][1];
            //printf("ans = %lld\n", ans);
            update(1, 1, m, 1, m, 0, i&1^1);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值