hdu 6992 / 2021“MINIEYE杯”中国大学生算法设计超级联赛(4)1008 Lawn of the Dead
https://acm.hdu.edu.cn/showproblem.php?pid=6992
题意:
n*m网格图上有k个障碍,初始位置(1,1),只能向下和向右走
问图上有多少个点是可以被走到的
数据范围1e5
n*m很大,但是障碍物比较少
考虑被2个障碍物夹住的区间[L,R],确定出这个区间最靠左的可以被走到的位置x,那么这个区间的贡献是R-x+1
x怎么确定?
因为之这是一段被2个障碍物夹住的区间,所以x不可能是从左边走过去的,那就只能是从上边走过去的
所以如果我们能够统计出上一行可以被走到的所有区间,从中找出与[L,R]有交集的区间[l,r],那[L,R]这段区间的贡献就是 R-max(L,l)+1
思路就是这样,关键是如何高效的实现
一是如何找出所有被障碍物夹住的区间
二是如何找相邻两行区间的交集
先把所有的障碍物以行位第一关键字,列位第二关键字排序
按行求解,先求本行的被障碍物夹住的区间,再求这些区间与上一行可行区间的交集
求本行的被障碍物夹住的区间就扫一遍本行的障碍物就行
求区间交集也类似,用2个位置指针,1个指向上一行可行区间用到哪一个,一个指向本行被障碍物夹住的区间用到哪一个
#include<bits/stdc++.h> using namespace std; #define N 100005 struct node { int x,y; node() {} node(int x_,int y_) { x=x_; y=y_;} }e[N]; vector<node>empty,f[2]; bool cmp(node p,node q) { if(p.x!=q.x) return p.x<q.x; return p.y<q.y; } int main() { int T,n,m,k,mine,l,r,line,tot1,tot2,p; long long ans; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=k;++i) scanf("%d%d",&e[i].x,&e[i].y); sort(e+1,e+k+1,cmp); mine=1; ans=0; line=0; f[0].clear(); f[line].push_back(node(1,1)); for(int i=1;i<=n;++i) { line^=1; empty.clear(); f[line].clear(); l=1; while(mine<=k && e[mine].x==i) { r=e[mine].y-1; if(r>=l) empty.push_back(node(l,r)); l=e[mine].y+1; ++mine; } if(l<=m) empty.push_back(node(l,m)); tot1=f[line^1].size(); tot2=empty.size(); p=0; for(int j=0;j<tot2;++j) { while(p<tot1 && f[line^1][p].y<empty[j].x) ++p; if(p==tot1 || f[line^1][p].x>empty[j].y) continue; l=max(f[line^1][p].x,empty[j].x); r=empty[j].y; f[line].push_back(node(l,r)); ans+=r-l+1; } } printf("%lld\n",ans); } }