zoukankan      html  css  js  c++  java
  • 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= sumlimits_{j=1}^i A_j)
    对于前(j)个数的某个划分,如果(sum_i-sum_j eq 0)的话,就可以将(A_{j+1} sim A_i)作为后缀拼在后面得到一个前(i)个数的划分
    所以转移方程为:(d_i = sumlimits_{j=0}^{i-1} d_j, sum_i - sum_{j-1} eq 0)

    优化(d_i)是由(d_j)拼接上不为(0)的后缀得到的,不如反向考虑,把所有可能的方案加起来然后减去那些后缀为(0)的不合法的方案。
    (cnt_s)表示元素之和为(s)的划分个数,如果某些划分和为(sum_i),说明它对应的后缀为(0)是不合法的,所以不合法的划分就有(cnt_{sum_i})个。
    因此(d_i=sumlimits_{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
    ", d[n]);
    
        return 0;
    }
    

    D. 矩形计数 容斥原理

    题意:

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

    分析:

    首先不考虑黑格子,计算出一共有多少个矩形:
    枚举矩形的大小(r imes 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
    ", ans);
    
    	return 0;
    }
    
  • 相关阅读:
    PHP比较操作符
    一个给图片加水印的程序
    PHP开发人员:充实您的XML工具箱
    PHP时间函数
    (Oralce)Web翻页优化实例
    PHP文件操作函数
    PHP图象函数
    PHP逻辑操作符
    PHP位操作符
    PHP目录遍历函数
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/6506255.html
Copyright © 2011-2022 走看看