1008 Lawn of the Dead 18.41%(405/2200)

题意:

  • 给出n*m的地图,k个地雷的坐标,从(1,1)出发,只能向下或向右走,求能走到多少个点。

思路1:

  • 考虑所有点的个数减去不能到达的点的个数,根据题意,有地雷的地方是不可以到达的。由于僵尸只会向右和向下走,当某个点的左边和上方都不可达时,该点不可达,并会对自己右边的点和下方的点造成影响
  • 由于空间很大但地雷数有限,可以从上往下逐行对每一行的地雷排序后进行处理。对每个地雷,找到从自己的右上角点(x-1, y+1) 开始的从左往右的连续不可达区域的范围,那么这行的这个范围也不可达。可以用线段树来实现区间查询和区间覆盖。每一行处理完后查询该行不可达的点数,累加后用总点数减即得到答案。

思路2:

  • 考虑直接计算能到达的点的个数,对于一行一段连续的区间,什么情况下我们才能走这段区间的点呢?可以发现,只要上面一段区间与当前判断区间有交集,那么我就能从上面那段区间下来,访问下面区间上的点。
  • 即对于当前区间找到上一行中与当前区间有交集的区间,①如果找不到,那么这段区间访问不到。②如果找到了,左端点取一个最大值(与上一行的交集区间),然后右端点就是原判定区间的右端点,(因为只能往下或往右走,所以此时左端点处不相交的部分就走不到了,但是右边是可以的),,保存这段区间,以便进行下一行的判断。可以直接二分求得上一行区间左端点恰好大于当前判定区间左端点的那个区间。
  • 这样的话,我们就能得到这样一条思路:①首先特判第一行,保存第一段可行区间②每一行都保存这一行的地雷坐标(注意是按行保存),最后每行再加入一个m + 1列的地雷以便判断最后一段区间③然后我们枚举行数,每次判断两个地雷之间的地雷即可
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
const int inf = 1e9+7;

vector<int>a[maxn];//保存每一行的地雷位置
struct node{int l, r;node(int l=0,int r=0):l(l),r(r){}};
vector<node>ok; //保存上一行的可行区间
bool check(node a, int l, int r){
if(a.l<=l)return a.r>=l;
else if(a.l<=r)return 1;
else return 0;
}
int calc(int st, int ed){
int l =0, r=ok.size()-1, ans=ok.size()-1;
while(l <= r){//二分到ans区间恰好在[st,ed]附近的
int mid= (l+r)/2;
if(ok[mid].l <= st)l=mid+1;//找anl>st的
else r=mid-1,ans=r;
}
int res=inf, tmp=st;
for(int i=ans-5; i <= ans+5; i++){//枚举前后区间
if(i<0 || i>=ok.size())continue;
if(check(ok[i],st,ed)){//判断是否有交集
tmp = max(tmp, ok[i].l);//与上一行的交集区间的左端点
res = min(res, tmp);//所有左端点里取最靠左的(能走的更多)
}
}
return res==inf?-1:res;
}

int main(){
ios::sync_with_stdio(0);
int T; cin>>T;
while(T--){
int n, m, k; cin>>n>>m>>k;
for(int i =1; i<=n; i++)a[i].clear();
for(int i =1; i<=k; i++){
int x, y; cin>>x>>y;
a[x].push_back(y);
}
for(int i=1; i <=n; i++){
a[i].push_back(m+1);
sort(a[i].begin(),a[i].end());
}
ok.clear();
ok.push_back(node(1,a[1][0]-1));
LL ans = ok[0].r-ok[0].l+1, st = 1;//当前左端点
for(int i = 2; i <= n; i++){
vector<node>cur;
st = 1;
for(int j = 0; j < a[i].size(); j++){
int ed = a[i][j];//当前右端点
if(st >= ed){st = ed+1; continue;}//得到一个区间
int st2=calc(st, ed-1);//如果有交集,返回新的左端点
if(st2 == -1){st = ed+1; continue;}//没有交集,找下一个区间
cur.push_back(node(st2,ed-1));//有交集,那么新的现在可以走的区间
ans += ed-st2;//更新答案
st = ed+1;
}
ok = cur;
}
cout<<ans<<"\n";
}
return 0;
}