[SDOI2011] 打地鼠

题面

    最简单暴力的办法就是分别枚举R和C,然后再暴力判断这组R和C是否可行,复杂度取决于你判断的暴力程度。。。。

    加入判断可以做到 O(N^2),那么就是总复杂度就是O(N^4)。

    先不说这是否能过,判断 O(N^2)该怎么做?

    这里我们用到了二维差分。因为二分差分可以把二维区间加变成只修改四个点,而前缀和就对应这个位置的原数,很适合做这个题。

    我们判断的时候就直接扫描,p[][]代表差分数组,s[][]代表每个位置现在的数是多少(实则就是p[][]的二维前缀和)。到一个位置(i,j),若a[i][j]<s[i][j],则无解,返回false;

否则要进行区间 [i~i+R,j~j+C]加 a[i][j]-s[i][j],通过修改p[][]实现。s[][]的计算公式为 s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+p[i][j],这个画画图就明白了了。

    其实我们相当于扫到每个(i,j)时就知道了左上角打在这有多少次。

    而且其实最后复杂度到不了 O(N^4),原因在于枚举R和C的时候,R*C必须是 ∑a[i][j] 的约数,又∵ <=1e9的数最多有1000左右的约数,且R和C都是<=100的数,所以最外层枚举R和C的复杂度不到1000,是可以过这个题的。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=105;

int n,m,t,a[N][N],p[N*2][N*2],ans=1;

inline bool check(int x,int y){
	memset(p,0,sizeof(p));
	
	for(int i=1,ad;i<=n;i++)
	    for(int j=1;j<=m;j++){
	    	p[i][j]+=p[i-1][j]+p[i][j-1]-p[i-1][j-1];
	    	if(p[i][j]>a[i][j]) return 0;
	    	
	    	ad=a[i][j]-p[i][j];
	    	p[i][j]+=ad,p[i][j+y]-=ad;
	    	p[i+x][j]-=ad,p[i+x][j+y]+=ad;
		}
	
	return 1;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++) scanf("%d",a[i]+j),t+=a[i][j];
	
	 
	for(int i=1,now;i<=n;i++) if(!(t%i)){
		now=t/i;
		for(int j=now==t?2:1;j<=m;j++) if(i*j>ans&&!(now%j)&&check(i,j)) ans=i*j;
	}
	
	printf("%d
",t/ans);
	return 0;
}

  

原文地址:https://www.cnblogs.com/JYYHH/p/11317350.html