【训练题48:想法 + 模拟】Lawn of the Dead | HDU6992 | 杭电多校四 08题

题意

思路

  • 什么? s t d std std 是线段树? 那直接暴打 s t d std std b u s h i bushi bushi
    首先范围比较大,网格开不下
    答案最大为 O ( n m ) O(nm) O(nm),答案肯定不是一格一格算的,是一坨一坨算的
    考虑土豆雷怎么限制我们的移动范围:
    请添加图片描述
  • 可以看到,把我们上一行的所有可行范围存成一段一段的连续区间
    当前行的暂时可行范围就是所有中间一段没有土豆雷的区间
    那么当前行的真实可行范围是什么呢?
    假设 上一行的一个可行范围为 [ L 1 , R 1 ] [L1,R1] [L1,R1],当前行的一个暂时可行范围为 [ L 2 , R 2 ] [L2,R2] [L2,R2]
    [ L 1 , R 1 ] ∩ [ L 2 , R 2 ] ≠ ∅ [L1,R1]\cap [L2,R2]\ne \varnothing [L1,R1][L2,R2]= ,那么当前行的真实范围为 [ max ⁡ { L 1 , L 2 } , R 2 ] [\max\{L1,L2\},R2] [max{L1,L2},R2]
  • 然后就一行一行去推。因为若某一行无土豆雷,那么区间便只剩下一个 [ 1 , m ] [1,m] [1,m],所以区间段数不会很多,是 O ( n + k ) O(n+k) O(n+k) 级别的

代码

  • 时间复杂度: O ( n + k log ⁡ k ) O(n+k\log k) O(n+klogk)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 1e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

struct node{
    int x,y;
    bool operator< (const node &ND)const{
        if(x != ND.x)return x < ND.x;
        return y < ND.y;
    }
}mine[MAX];
struct node2{
    int zuo,you;
};
vector<node2>aa[2],lei;		// aa[st] 上一行真实区间 aa[st^1] 当前真实行区间 lei 当前行暂时可行区间
int main()
{
    int T;T = read();
    while(T--){
        int n,m,k;
        n = read();m = read();k = read();
        for(int i = 1;i <= k;++i){
            mine[i].x = read();
            mine[i].y = read();
        }
        sort(mine+1,mine+1+k);
        int now = 1;
        ll ans = 0;
        int st = 0;
        aa[st].clear();
        aa[st^1].clear();

        aa[st].push_back((node2){1,1});
        for(int i = 1;i <= n;++i){
            lei.clear();
            if(mine[now].x == i){			//	把这一行的暂时可行区间拿出来
                int las = 1;
                while(now <= k && mine[now].x == i){
                    if(las <= mine[now].y-1)lei.push_back((node2){las,mine[now].y-1});
                    las = mine[now].y + 1;
                    now++;
                }
                if(las <= m)lei.push_back((node2){las,m});
            }else{
                lei.push_back((node2){1,m});
            }
//            show("i = " , i);
//            for(auto it : lei){
//                show(it.zuo,it.you);
//            }

//            show("i = " , i);
//            for(auto it : aa[st]){
//                show(it.zuo,it.you);
//            }
            int shu1 = aa[st].size();
            int shu2 = lei.size();
            int tmp = 0;
            for(int j = 0;j < shu2;++j){
                while(tmp < shu1 && aa[st][tmp].you < lei[j].zuo)tmp++;		// 对于每一个暂时可行区间,去做操作
                if(tmp == shu1)break;
                int l = max(aa[st][tmp].zuo,lei[j].zuo);
                int r = min(aa[st][tmp].you,lei[j].you);
                int r2= lei[j].you;
                if(l <= r){
                    aa[st^1].push_back((node2){l,r2});
                    ans += (r2 - l + 1);
                }
            }
            aa[st].clear();
            st ^= 1;		// 二维数组滚动一下

        }
        Print(ans,'\n');
    }
    Write();
    return 0;
}
/**
3
4 4 4
2 1
2 2
2 3
2 4

4 4 3
4 1
3 2
4 3

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值