1. 程式人生 > >noip模擬賽 入陣曲

noip模擬賽 入陣曲

處理 固定 前綴 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>

using
namespace 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模擬賽 入陣曲