#C241128B. 梦相遇


#C241128B. 梦相遇

标签(Label)

  • 二分答案
  • 深搜

网址(Website)

题目详情 - 梦相遇 - Super

题解 - 梦相遇 - Super

题目(Problem)

输入数据 1

1 4
3 3 1
3 3
3 5 4
1 1
1 2
1 5
3 3
3 5 5
1 1
1 2
1 5
2 4
3 3
6 6 6
1 1
1 2
2 1
5 6
6 5
6 6

输出数据 1

2
1
-1
3

样例 2

见选手目录下的 $meet/meet2.in$ 与 $meet/meet2.ans$。

该样例满足测试点 $9, 10$ 的限制。

样例 3

见选手目录下的 $meet/meet3.in$ 与 $meet/meet3.ans$。

该样例满足测试点 $1 \sim 3$ 的限制。

样例 4

见选手目录下的 $meet/meet4.in$ 与 $meet/meet4.ans$。

该样例满足测试点 $8$ 的限制。

样例 5

见选手目录下的 $meet/meet5.in$ 与 $meet/meet5.ans$。

该样例满足测试点 $12$ 的限制。

样例 6

见选手目录下的 $meet/meet6.in$ 与 $meet/meet6.ans$。

该样例满足测试点 $18 \sim 20$ 的限制。

题解(Solution)

由于求得是最大,很容易发现答案单调性,直接二分答案。

接下来考虑的就是在 $O(nm)$ 时间内计算出当前的 $mid$ 能否满足条件。

先考虑怎么放矩阵,很明显可以直接记录二维前缀和,然后求出区间内是否有禁行区。

后面考虑直接 $\text{dfs}$ 遍历,怎么记录这个矩阵呢?本来想的是记录四个角,其实因为长宽已知,只需要记录左上角就好了,遍历的时候判断四个角是否满足条件即可。

这里提几个需要注意的点,因为题目的限制是 $n\times m\le3\times10^6$ ,因此我使用的是指针来表示矩阵,原来的代码是计算的每一行 $m$ 个元素,但是发现我使用矩阵的时候需要用到诸如 $(n+1,1),(1,m+1),(0,0)$ 这些位置,因为一行只有 $m$ 个元素,因此访问的时候总是会访问到前面的位置,导致最后答案出错。因此,一行至少要设成 $(m+2)$ 个元素,防止访问的时候炸掉。我可是多打了 $\text{1.5k}$ 的代码才发现这个问题。

正确的打法应该是这样:

struct Wolf{
	int v[N];
	int *operator[](const int x){return v+x*(m+2);}
	void clear(int n,int m){memset(v,0,(n+2)*(m+2)*sizeof(v[0]));}
}c,d;

后面又调了几十分钟的代码,才发现是求前缀和求错了,原来我用的是 $x_1,y_1,x_2,y_2$ ,后面害怕用到关键字又改成了 $x,y,i,j$ ,结果恰巧发现了这个问题——我把 -c[x1-1][y2] 写成了 -c[x1-1][y1] ,换成 $i,j$ 之后就很容易发现 -c[i-1][j] 不对,之后还是要用有区分度的变量,要不然容易看错,还找不到。

(所以就不理解为什么要用 $u,v$ 作为点,这两个东西是最容易看错的)

时间复杂度: $O(S\log S)$ 。

出题者题解

代码(Code)

100分
#include<bits/stdc++.h>
#include<vector>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
#define show(a,n) {For(o,1,n) cerr<<a[o]<<' ';cerr<<'\n';}
using namespace std;
#define P pair<int,int>
#define int long long
#define x first
#define y second
#define in(x,l,r) (l<=x && x<=r)
inline int rd(){
	char c;bool f=false;while(!isdigit(c=getchar()))f=c=='-';int x=c^48;
	while(isdigit(c=getchar())){x=(((x<<2)+x)<<1)+(c^48);}return f?-x:x;
}template<typename G>
void write(G x){
	if(x<0) putchar('-'),write(-x);
	else if(x<=9) putchar('0'+x);
	else write(x/10), putchar('0'+x%10);
}constexpr int inf = 0x3f3f3f3f3f3f3f3fll;
constexpr int N = 10123456;
bool ST;int Tt,tid;

/*
答案单调性->二分答案 
转换为知道矩形大小,如何在O(nm)时间内得出是否可行
O(nm)放矩阵?直接判断是否能放 
记录矩阵四个角,dfs搜索上下左右,除去最开始覆盖了x*x的方格,还要覆盖n*m-x*x-ban个方格。 
处理已经走过的位置的问题,由于形状固定只需要记录左上角的点即可
如何判断是否走完了所有的点?每次走的时候记录前缀和表示该段被覆盖,
最后O(nm)遍历全图求前缀和,如果有位置没有被禁用且前缀和为0,那么不行 
*/

int n,m,k;
struct Wolf{
	int v[N];
	int *operator[](const int x){return v+x*(m+2);}
	void clear(int n,int m){memset(v,0,(n+2)*(m+2)*sizeof(v[0]));}
}c,d;
inline int ask(int i,int j,int x,int y){
	return c[x][y]-c[x][j-1]-c[i-1][y]+c[i-1][j-1];
}

struct Deer{
	bool v[N];
	bool *operator[](const int x){return v+x*(m+2);}
	void clear(int n,int m){memset(v,0,(n+2)*(m+2)*sizeof(v[0]));}
}a,vst;

int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
void dfs(int x,int y,int md){
	d[x][y]++, d[x+md][y+md]++;
	d[x][y+md]--, d[x+md][y]--;
	vst[x][y] = true;
	For(k,0,3){
		int i=x+dx[k], j=y+dy[k];
		if(!vst[i][j] && in(i,1,n) && in(j,1,m) && in(i+md-1,1,n) && in(j+md-1,1,m) && !ask(i,j,i+md-1,j+md-1))
			//如果没有走过 且 矩阵在合理范围内 且 没有覆盖禁行点
			dfs(i,j,md);
	}
}

bool check(int mid){
	vst.clear(n,m), d.clear(n,m);
	
	bool flag = true;
	for(int i=1;i<=n-mid+1&&flag;i++){
		for(int j=1;j<=m-mid+1&&flag;j++){
			if(ask(i,j,i+mid-1,j+mid-1)==0)//如果中间没有禁行区
				dfs(i,j,mid),flag=false;
	}}if(flag) return false;
	
	For(i,1,n) For(j,1,m){
		d[i][j] += d[i][j-1]+d[i-1][j]-d[i-1][j-1];
		if(!d[i][j] && !a[i][j]) return false;
	}return true;
}

void Solve(){
	a.clear(n,m), c.clear(n,m);
	
	n=rd(),m=rd(),k=rd();For(i,1,k) a[rd()][rd()] = 1;
	For(i,1,n) For(j,1,m) c[i][j]=c[i-1][j]+c[i][j-1]-c[i-1][j-1]+a[i][j];
	
	int l=1, r=min(n,m), mid, res=-1;
	while(l<=r){
		mid = (l+r)>>1;
		if(check(mid)) l=mid+1, res=mid;
		else r=mid-1; 
	}
	
	if(~res) write(res), putchar('\n');
	else write(-1), putchar('\n'); 
}

bool ED;
signed main(){
	cerr<<abs(&ST-&ED)/1024./1024.<<"MB\n";
	freopen("meet.in","r",stdin);
	freopen("meet.out","w",stdout);
	tid=rd(),Tt=rd();double Tim=clock();
	while(Tt--) Solve();
	return cerr<<"TIME:"<<(clock()-Tim)/CLOCKS_PER_SEC,0;
}

文章作者: WolfDeer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WolfDeer !
  目录