二分 + 前缀和 - Monitor - CodeForces 846D
题意:
给定一个n×m的矩阵,其中有q个点是坏掉的,
当整个矩阵中存在某个k×k的子矩阵中的点都坏了,那么整个矩阵就会损坏。
q个点按照一定的时间顺序损坏,
判断最早在何时,整个矩阵会损坏,即出现k×k个子矩阵内的点均损坏的情况。
若不存在整个矩阵损坏的情况,输出−1。
输入:
首行输入四个正整数,n,m,k,q
接着q行,每行包括三个正整数xi,yi,ti,
表示(xi,yi)处的点在ti时刻损坏。
输出:
一个正整数,表示答案。
Examples
Input
2 3 2 5
2 1 8
2 2 8
1 2 1
1 3 4
2 3 2
Output
8
Input
3 3 2 5
1 2 2
2 2 1
2 3 5
3 2 10
2 1 100
Output
-1
数据范围:
1 ≤ n, m ≤ 500, 1 ≤ k ≤ min(n, m), 0 ≤ q ≤ n⋅m,1 ≤ xi ≤ n, 1 ≤ yi ≤ m, 0 ≤ ti ≤ 109
分析:
求时间的最小值,而时间是满足单调性的,故我们可以二分答案。
如何check?
首先我们对q个点,按照时间顺序从小到大排序,
对于每一个时间mid,我们用二维数组a,将所有在mid时间以内损坏的点上累加1,
然后对数组a求一遍前缀和,
最后全图枚举k×k的子矩阵,查询子矩阵中损坏点的数量,若数量等于k×k,则说明mid可行。
二分的边界:
左端点l=0是可能取到的,
右端点r应当取q个点中的最大时间tn,再加1,即r=tn+1
因为无解的情况下,l最终会增加到r的初始值,
+1是为了区分无解的情况,因答案是可能取到tn的。
时间复杂度:
O(nmlogt)
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
const int N=510;
struct node
{
int x,y,t;
bool operator < (const node s) const
{
return t<s.t;
}
}T[N*N];
int n,m,k,q;
int sum[N][N];
bool check(int mid)
{
static int a[N][N];
memset(a,0,sizeof a);
memset(sum,0,sizeof sum);
int idx=0;
while(idx<=q && T[idx].t<=mid)
{
a[T[idx].x][T[idx].y]=1;
++idx;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
for(int i=k;i<=n;i++)
for(int j=k;j<=m;j++)
{
int x=i-k+1,y=j-k+1;
if(sum[i][j]-sum[x-1][j]-sum[i][y-1]+sum[x-1][y-1]==k*k)
return true;
}
return false;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&q);
for(int i=0;i<q;i++) scanf("%d%d%d",&T[i].x,&T[i].y,&T[i].t);
sort(T,T+q);
int l=0,r=T[q-1].t+1;
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
if(l>T[q-1].t) puts("-1");
else printf("%d\n",l);
return 0;
}