k 倍子矩形(枚举暴力)

题目放在洛谷U188718,出处在 SDFZ-NOIP2021 模拟 11-T2。

显然的暴力是 \(\mathcal{O(n^4)}\),预处理二维前缀和,枚举左上角、右下角,期望得分 \(60\),具体因实现而异。

观察数据范围发现 \(n\le 400\),只需要优化到 \(\mathcal{O(n^3)}\) 即可。考虑 \(n^2\) 地枚举一个纵向区间 \([l,r]\),如下图所示:

image

对于这个区间,用指针从左向右扫,记录前缀矩形面积 \(c\)。在已经预处理了纵向前缀和的基础上,这是 \(\mathcal{O(m)}\) 的。在模 \(k\) 意义下,如果某时刻的 \(c\) 和之前一时刻的 \(c'\) 相等,则这一段矩形面积一定能被 \(k\) 整除。我们不妨开一组桶,记录每个相同的 \(c\) 值出现了多少次,最后扫一遍 \(1\to k\),统计答案。

还是以上图为例,红色的线是指针,从左往右扫。在 \(i\) 的位置,\(c=S_{绿色矩形}\)\(c\equiv m(\mod k)\);走到 \(j\) 的位置,\(c=S_{蓝色矩形}\),恰巧又有 \(c\equiv m(\mod k)\)。因此 \(k\vert S_{红色矩形}\),答案++……扫完之后,\(\mod k\) 结果为 \(m\) 的结果记为 \(cnt_m\),其对答案的总贡献为 \(cnt_m\times (cnt_m-1)/2\)

特别地,我们应当初始化 \(cnt_0\gets 1\),否则如果一个矩形本身面积为 \(k\) 的倍数,那它自己的 \(1\) 发贡献将被漏算,导致 WA 穿地心。

下面是 AC 代码:

int main(){
	int n,m,k; read(n),read(m),read(k);
	rep(i,1,n) rep(j,1,m){
		int x; read(x);
		s[i][j]=(s[i-1][j]+x)%k;//对竖列求前缀和
	}
	long long ans=0;
	rep(i,1,n) rep(j,i,n){ //枚举区间(纵向)
		int c=0,t=1; cnt[0]=1,v[1]=0;
		rep(p,1,m){//指针扫描(横向)
			if((c+=s[j][p]-s[i-1][p])<0) c+=k;
			if(c>=k) c-=k;//相当于取模
			!cnt[c]?cnt[v[++t]=c]=1:++cnt[c];//扔进桶里
		}
		rep(p,1,t){
			int& x=cnt[v[p]];//没别的意思,压行罢了
			ans+=x*(x-1)/2,x=0;
		}
	}
	write(ans),EN;
}

THE END

posted @ 2021-11-11 21:02  q0000000  阅读(37)  评论(0编辑  收藏  举报