noip模擬賽 入陣曲
阿新 • • 發佈:2017-11-07
處理 固定 前綴 ret lag n) bool 求和 noi
分析:其實很容易想到O(n^3m^3)的算法,枚舉x1,x2,y1,y2,再統計一下和.求和可以用前綴和,能優化到O(n^2m^2),能得到60分.對於特殊性質的點,求一下a[i][j]與k的最小公倍數lcm,就可以推出來要選多少個點,乘法原理推一下就能解決了.
滿分做法的思想是降維,先分析一下一維怎麽做.問題要求滿足(a[l] + a[l + 1] + ...... + a[r]) % k = 0的區間[l,r]有多少個.利用前綴和優化就是(sum[r] - sum[l - 1]) % k = 0.對約束進行變形:sum[r] % k = sum[l - 1] % k. O(n)的掃一遍,記錄當前的sum[i] % k,看前面有多少個和它相同的就可以了.
轉化到二維上,為了用上一維的做法,固定矩形的上邊界和下邊界,把每一列看做是一個元素a[i],就可以用上一維的做法了,是一個非常常見的變形.
求子矩陣問題的常用思路是先轉化到1維上進行處理,再把行或列壓一下,就能把2維放到1維上處理了,數學式子一定要會變形!
75分暴力:
#include <cmath> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> usingnamespace std; typedef long long ll; ll n, m, k, a[410][410], sum[410][410], t, ans; bool flag = true; void solve2() { for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int p = i; p <= n; p++) for (int q = j; q <= m; q++) { ll temp= sum[p][q] - sum[i - 1][q] - sum[p][j - 1] + sum[i - 1][j - 1]; if (temp % k == 0) ans++; } printf("%lld\n", ans); } ll gcd(ll a, ll b) { if (!b) return a; return gcd(b, a % b); } void solve1() { ll lcm = a[1][1] / gcd(a[1][1], k) * k; ll res = lcm / a[1][1]; ll temp = 0; while (res <= n * m) { temp = 0; for (ll i = 1; i <= n; i++) if (res % i == 0 && (res / i) <= m) temp += (n - i + 1) * (m - res / i + 1); //printf("%lld %lld\n", res, temp); ans += temp; res += lcm / a[1][1]; } printf("%lld\n", ans); } int main() { scanf("%lld%lld%lld", &n, &m, &k); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { scanf("%lld", &a[i][j]); if (!(i == 1 && j == 1) && a[i][j] != t) flag = false; t = a[i][j]; sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j]; } if (flag) solve1(); else solve2(); return 0; }
正解:
#include <cmath> #include <vector> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; ll n, m, k, a[410][410], sum[410][410], ans, cnt[1000010]; int main() { scanf("%lld%lld%lld", &n, &m, &k); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { scanf("%lld", &a[i][j]); sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j]; } for (int i = 0; i < n; i++) for (int j = i + 1; j <= n; j++) { cnt[0] = 1; for (int kk = 1; kk <= m; kk++) { ll p = (sum[j][kk] - sum[i][kk] + k) % k; ans += cnt[p]; cnt[p]++; } for (int kk = 1; kk <= m; kk++) cnt[(sum[j][kk] - sum[i][kk] + k) % k] = 0; } printf("%lld\n", ans); return 0; }
noip模擬賽 入陣曲