[CSP-S模拟测试]:physics(二维前缀和+二分+剪枝)

题目传送门(内部题26)


输入格式

第一行有$3$个整数$n,m,q$。
然后有$n$行,每行有一个长度为$m$的字符串,$+$表示正电粒子,$-$表示负电粒子。
然后有$q$行,每行$2$个整数$x,y$,表示将第$x$行第$y$列的正电粒子修改为负电粒子,保证修改前第$x$行第$y$列的粒子带正电。


输出格式

有$q$行,每行一个整数此次修改后所有正电粒子能形成的最大的电场强度。


样例

样例输入:

5 5 5
+-+++
+++++
+++++
+++++
++++-
1 5
2 2
5 3
2 3
1 1

样例输出:

4
3
3
2
2


数据范围与提示

对于所有数据,$1\leqslant n,m\leqslant {10}^3,1\leqslant q\leqslant {10}^3$。


题解

$40\%$算法:

纯暴力,暴力枚举端点,暴力枚举边长,暴力统计。

时间复杂度:$\Theta(n^5)$。

期望得分:$40$分。

实际得分:$40$分。

$70\%$算法:

如果你会二维前缀和,然后你以为你可以拿到$70$分,然后……依然是$40$分。

但是我们发现,假设当前枚举的端点是$(i,j)$,边长为$k$,那么如果当前的正方形不行,那边长更大的肯定也不行;如果行,边长更小的肯定也行,那么我们要找的答案就在这行与不行的分界点上,而这个分界点你可以通过二分来找。

时间复杂度:$\Theta(n^2\log n)$。

期望得分:$40$分。

实际得分:$40$分。

$70\%pro$算法:

题目中只会把正电子变为负电子,所以最大的电场强度一定是单调不递增的。

进而,如果我们现在改变的这个电荷不在最大的正方形内,也就是它的改变对答案并没有影响,那么我们就可以标记它改变了,然后输出上一次的答案。

如果它在最大正方形里,那么暴力再来一遍好啦。

在二分的时候,如果已经不能出现比现在已经计算出来的答案更大的答案,就直接$continue$掉好啦。

由于强大的剪枝,你就可以用这个算法$A$掉这道题了,但是仍能被极端数据卡掉。

时间复杂度:$\Theta(n^2\log n)$。

期望得分:$70$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,m,q;
char ch[1001];
int Map[1001][1001],sum[1001][1001];
int maxn,ans=1001;
pair<int,int> pre;
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	maxn=min(n,m);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=m;j++)
		{
			if(ch[j]=='+')Map[i][j]=1;
			sum[i][j]=Map[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
		}
	}
	while(q--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		Map[x][y]=0;
		if(x<pre.first||y<pre.second||x>pre.first+ans-1||y>pre.second+ans-1)
		{
			printf("%d\n",ans);
			continue;
		}
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				sum[i][j]=Map[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
		ans=0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				int lft=1,rht=min(maxn,min(n-i+1,m-j+1)),res=0;
				if(rht<=ans)break;
				while(lft<=rht)
				{
					int mid=(lft+rht)>>1;
					if(sum[i+mid-1][j+mid-1]-sum[i+mid-1][j-1]-sum[i-1][j+mid-1]+sum[i-1][j-1]==mid*mid){lft=mid+1,res=mid;}
					else rht=mid-1;
					if(rht<=ans)break;
				}
				if(res>ans)
				{
					ans=res;
					pre=make_pair(i,j);
				}
			}
		printf("%d\n",ans);
		maxn=ans;
	}
	return 0;
}

rp++

posted @ 2019-09-06 14:09  HEOI-动动  阅读(233)  评论(0编辑  收藏  举报