题解 入阵曲

传送门

忘删调试信息 55pts -> 5pts
以后交代码之前ctrl+f查一遍cout删没删
以后写代码不要为了方便对拍同时在a.cpp,t1.cpp,my.cpp里面写,
不光容易交错代码,也容易写乱

首先一个\(O(n^4)\)的二维前缀和优化很好想
然后考虑优化,考场上思路就卡在这了
暴力需要枚举矩形的两个顶点
注意到计算矩形数字和:\(dp[x2][y2]-dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1]\)
我想的是能统计出一个数组\(cnt[i][j]\)表示从左上角到(但不包括)\(i\)\(j\)间,
对于每个余数,有多少个\((dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1])mod\ k\)恰好等于这个余数
然后不考虑预处理的话可以\(O(n^2)\)转移
然而这个\(cnt\)数组几乎不满足转移,如果强行转移的话还需要线段树合并和一堆标记
就很不好办了

其实这样想麻烦了
还是考虑处理出一个\(cnt\)数组
二维的\(cnt\)数组不好转移,考虑只构建一维的
那么这样的话两个矩形需要有一个在同一行/列的边界点
枚举矩形的另一种方式:
固定矩形的宽(即上下边界),依次\(n^2\)枚举左右边界
那这里对于每一个宽重新构建一个\(cnt\)数组,根本没必要转移
枚举宽度,纵边界,横边界都是\(O(n)\),总复杂度\(O(n^3)\)

还有这里特殊性质也没骗到
考虑面积,但这里不必枚举矩形,可以枚举面积
统计出每个满足\(s*a[i][j]\equiv 0\pmod{k}\)的面积\(s\)对应多少个矩形,加和即可

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 410
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m, k;
int mat[N][N], last=-1;
ll ans=0;
bool flag=1;

namespace force{
	int dp[N][N];
	inline int calc(int x1, int y1, int x2, int y2) {
		if (x1==x2&&y1==y2) return -1;
		return (dp[x2][y2]-dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1])%k;
	}
	void solve() {
		for (int i=1; i<=n; ++i) 
			for (int j=1; j<=m; ++j) 
				dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+mat[i][j]);
		//for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) cout<<dp[i][j]<<' '; cout<<endl;}
		for (int li=0; li<n; ++li) 
			for (int i=li+1; i<=n; ++i) 
				for (int lj=0; lj<m; ++lj) 
					for (int j=lj+1; j<=m; ++j) {
						if (!calc(i-li, j-lj, i, j)) ++ans;
						//x1=i-li; x2=li; y1=j-lj; y2=lj;
						//cout<<i-li<<' '<<j-lj<<' '<<i<<' '<<j<<' '<<calc(i-li, j-lj, i, j)<<endl;
					}
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task1{
	inline int calc(int x1, int y1, int x2, int y2) {
		return (x2-x1+1)*(y2-y1+1)%k*last%k;
	}
	void solve() {
		//for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) cout<<dp[i][j]<<' '; cout<<endl;}
		for (int li=0; li<n; ++li) 
			for (int i=li+1; i<=n; ++i) 
				for (int lj=0; lj<m; ++lj) 
					for (int j=lj+1; j<=m; ++j) {
						if (!calc(i-li, j-lj, i, j)) ++ans;
						//x1=i-li; x2=li; y1=j-lj; y2=lj;
						//cout<<i-li<<' '<<j-lj<<' '<<i<<' '<<j<<' '<<calc(i-li, j-lj, i, j)<<endl;
					}
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task{
	int dp[N][N], cnt[1000010], sta[1000010], top=0;
	inline int calc(int x1, int y1, int x2, int y2) {
		return ((dp[x2][y2]-dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1])%k+k)%k;
	}
	inline void clear() {while (top) cnt[sta[top--]]=0;}
	void solve() {
		for (int i=1; i<=n; ++i) 
			for (int j=1; j<=m; ++j) 
				dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+mat[i][j])%k;
		for (int li=0,t,t2; li<n; ++li) 
			for (int i=li+1; i<=n; ++i) {
			clear();
			cnt[0]=1;
				for (int j=1; j<=m; ++j) {
					t = calc(i-li, 1, i, j);
					ans += cnt[t]; //, cout<<"+= "<<cnt[t]<<endl;
					if (!cnt[t]) sta[++top]=t;
					++cnt[t];
					//cout<<i-li<<' '<<1<<' '<<i<<' '<<j<<' '<<calc(i-li, 1, i, j)<<endl;
				}
			}
		printf("%lld\n", ans);
		exit(0);
	}
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	n=read(); m=read(); k=read();
	for (int i=1; i<=n; ++i) 
		for (int j=1; j<=m; ++j) {
			mat[i][j]=read();
			//if (mat[i][j]==k) ++ans;
			//if (last==-1) last=mat[i][j];
			//else if (flag && mat[i][j]!=last) flag=0;
		}
	//cout<<"flag: "<<flag<<endl;
	
	task::solve();
	
	//if (!flag) force::solve();
	//else task1::solve();

	return 0;
}
posted @ 2021-06-30 10:52  Administrator-09  阅读(19)  评论(0编辑  收藏  举报