题意
- 【2021“MINIEYE杯”中国大学生算法设计超级联赛(4)】Lawn of the Dead | HDU6992
n
×
m
n\times m
n×m 的网格
有
k
k
k 个土豆雷,坐标为
x
i
,
y
i
x_i,y_i
xi,yi
有一个僵尸在
(
1
,
1
)
(1,1)
(1,1)
一次移动可以向右 / 向下移动一格,但他不能走到土豆雷上,最后走到不能走为止。
他有多少个格子是能够走得到的? -
1
≤
n
,
m
,
k
≤
1
0
5
1\le n,m,k\le 10^5
1≤n,m,k≤105
思路
- 什么?
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;
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});
}
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;
}