洛谷P3941 入阵曲

题目链接

入阵曲

solution

  • 很容易想到预处理二维前缀和%k的余数,然后\(O(n^4)\)判断
  • 考虑如何优化到\(O(n^3)\)
  • 我们可以枚举子矩形的起点的\(x\)坐标与水平长度,于是问题就可转化为一维
  • 在一维平面上,一个子矩阵的和可以转化为一个大的前缀矩阵和减去一个小的,显然只要这2个矩阵和%k的余数相同,对应的子矩阵就符合条件
  • 于是可以\(O(n)\)扫一遍记录%k余\(i\)的前缀矩阵和有多少个,即可\(O(n^3)\)完成

code

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
typedef long long ll;
const int N=450;
const int K=1e6+10;
int n,m,k,a[N][N],s[N][N],w[K];
ll ans;
inline int add(int x,int y){return (x+y>=k)?x+y-k:x+y;}
inline int dec(int x,int y){return (x-y<0)?x-y+k:x-y;}
int main(){
//	freopen("rally.in","r",stdin);
//	freopen("rally.out","w",stdout);
	n=read();m=read();k=read();
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
			s[i][j]=read(),s[i][j]%=k;
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
			s[i][j]=dec(add(s[i][j-1],add(s[i-1][j],s[i][j])),s[i-1][j-1]);
	}
	for(int i=0;i<=m;++i){
		for(int j=i+1;j<=m;++j){
			for(int ww=0;ww<=n;++ww){
				int nw=dec(s[ww][j],s[ww][i]);
				ans+=w[nw];
				++w[nw];
			}
			for(int ww=0;ww<=n;++ww){
				int nw=dec(s[ww][j],s[ww][i]);
				--w[nw];
			}
		}
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/tqxboomzero/p/13836939.html