zoukankan      html  css  js  c++  java
  • noip模拟赛 入阵曲

    分析:其实很容易想到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
    ", 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
    ", res, temp);
            ans += temp;
            res += lcm / a[1][1];
        }
        printf("%lld
    ", 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
    ", ans);
    
        return 0;
    }
  • 相关阅读:
    POJ 2411 Mondriaan's Dream -- 状压DP
    codeforces 792A-D
    codeforces 796A-D
    Acdream1201 SuSu's Power
    HDU 2818 Building Block
    C# NetStream
    基于Duff's Device的C简易无栈协程实现
    CentOS 多版本 GCC 共存
    2017杭电多校第一场
    2019杭电多校第十场
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7794505.html
Copyright © 2011-2022 走看看