Hihocoder [Offer收割]编程练习赛8

A. 小Ho的强迫症 数论 GCD

题意:

一个人在无限长的路上走格子,步长为\(D\),脚长为\(F\),每个格子的长度为\(L\),如图所示:

问能否一直走下去,永远踩不到线。

分析:

先不考虑脚的长度,观察一下在所有格子中落点的情况:
容易知道在每个格子中的落点到格子起点的距离都是\(g\)\(2g\)\(3g \cdots\),其中\(g=gcd(D, \; L)\)
即落点以\(g\)等间距地分布在格子上,所以如果脚长\(F>g\),一定会踩到格子的边线。

#include <cstdio>

int gcd(int a, int b) { return !b ? a : gcd(b, a%b); }

int main()
{
    int T; scanf("%d", &T);
    while(T--) {
        int L, F, D; scanf("%d%d%d", &L, &F, &D);
        int g = gcd(L, D);
        if(F <= g) puts("YES");
        else puts("NO");
    }

    return 0;
}

B. 拆字游戏 BFS

题意:

有一个\(01\)矩阵,求出所有四方向的连通块并输出。

分析:

找连通块可以用BFS,主要说一下这题的坑点:

  • 代表点是连通块的最左且最上的点,而不是所在矩阵的左上角。
  • 输出连通块所在矩阵时,注意不要输出其他连通块的\(1\)

代码丑拒贴,(ˉ▽ ̄~)


C. 数组分拆 DP

题意:

给出一个长度为\(N(N \leq 10^5)\)的数组\(A_1 \sim A_N,|Ai| \leq 100\),求有多少种方案可以将数组划分若干段,且每段之和不为0,方案数对\(10^9 + 7\)取模。

分析:

\(d_i\)为将前\(i\)个数划分为若干段且每段之和不为\(0\)的方案数
容易想到一个\(O(N^2)\)的做法:
预处理一下前缀和\(sum_i= \sum\limits_{j=1}^i A_j\)
对于前\(j\)个数的某个划分,如果\(sum_i-sum_j \neq 0\)的话,就可以将\(A_{j+1} \sim A_i\)作为后缀拼在后面得到一个前\(i\)个数的划分
所以转移方程为:\(d_i = \sum\limits_{j=0}^{i-1} d_j, sum_i - sum_{j-1} \neq 0\)

优化\(d_i\)是由\(d_j\)拼接上不为\(0\)的后缀得到的,不如反向考虑,把所有可能的方案加起来然后减去那些后缀为\(0\)的不合法的方案。
\(cnt_s\)表示元素之和为\(s\)的划分个数,如果某些划分和为\(sum_i\),说明它对应的后缀为\(0\)是不合法的,所以不合法的划分就有\(cnt_{sum_i}\)个。
因此\(d_i=\sum\limits_{j=1}^{i-1} d_j - cnt_{sum_i}\)

边界情况是\(d_0=1\),对应\(A_1 \sim A_i\)作为一个整体划分的转移

#include <cstdio>
#include <map>
using namespace std;

typedef long long LL;
const LL MOD = 1000000007LL;
void add(LL& a, LL b) { a += b; if(a >= MOD) a -= MOD; }

map<int, LL> M;

const int maxn = 100000 + 10;

int n, a[maxn], sum[maxn];
LL d[maxn];

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        sum[i] = sum[i - 1] + a[i];
    }
    
    M[0] = 1;
    d[0] = 1;
    LL tot = 1;
    for(int i = 1; i <= n; i++) {
        d[i] = (tot + MOD - M[sum[i]]) % MOD;
        add(M[sum[i]], d[i]);
        add(tot, d[i]);
    }

    printf("%lld\n", d[n]);

    return 0;
}

D. 矩形计数 容斥原理

题意:

有一个\(N\)\(M\)列的矩形,其中有\(K\)个黑格子,其余的都是白格子。
求全部由白格子组成的矩形的个数。

分析:

首先不考虑黑格子,计算出一共有多少个矩形:
枚举矩形的大小\(r \times c\),这样大小的矩形一共有\((N-r+1)(M-c+1)\)个。
然后减去不符合要求的矩形,也就是减去包含第一个黑格子的矩形个数,减去包含第二个黑格子,第三个的……
然后再加上包含第一第二黑格子的矩形数……
也就是容斥原理。

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;

int n, m, k;
int r[10], c[10];

int main()
{
    scanf("%d%d%d", &n, &m, &k);

    LL ans = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            ans += (LL)(n + 1 - i) * (m + 1 - j);
    for(int i = 0; i < k; i++)
        scanf("%d%d", r + i, c + i);
    for(int S = 1; S < (1 << k); S++) {
        int lft = m + 1, top = n + 1;
        int down = 0, rgh = 0;
        int cnt = 0;
        for(int i = 0; i < k; i++) if((S >> i) & 1) {
            cnt++;
            lft = min(lft, c[i]);
            top = min(top, r[i]);
            rgh = max(rgh, c[i]);
            down = max(down, r[i]);
        }
        int sign = (cnt & 1) ? -1 : 1;
        ans += (LL)sign * lft * (m + 1 - rgh) * top * (n + 1 - down);
    }

    printf("%lld\n", ans);

    return 0;
}
posted @ 2017-03-06 15:07  AOQNRMGYXLMV  阅读(...)  评论(... 编辑 收藏