前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >挑战程序竞赛系列(47):4.1 计数 二项式取模

挑战程序竞赛系列(47):4.1 计数 二项式取模

作者头像
用户1147447
发布2019-05-26 09:41:47
3710
发布2019-05-26 09:41:47
举报

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1434717

挑战程序竞赛系列(47):4.1 计数 二项式取模

详细代码可以fork下Github上leetcode项目,不定期更新。

练习题如下:

AOJ 2214: Warp Hall

题意参考博文:

http://www.hankcs.com/program/algorithm/aoj-2214-hall-warp.html

思路:

hankcs讲的很清楚,先对虫洞的起始位置排序,接着用一个dpi表示到达第i个虫洞起始位置所需要的方案数。

首先,如果不考虑i前面的虫洞,它的方案数有C(x1+y1,x1)C(x1 + y1, x1),现在在i前面有i-1个虫洞,该如何组合呢?

可以分为两种情况:

  • 第i个虫洞的起始位置大于所有前i-1个虫洞的终点位置,那么递推式为:
dp[i] = dp[i] + dp[j] * (j终点到i起点的方案数 - j起点到i起点的方案数) j = 0, 1, ..., i - 1;
  • 第i个虫洞的起始位置小于前i-1中某个虫洞的终点位置,那么递推式为:
dp[i] = dp[i] + dp[j] * (0 - j起点到i起点的方案数) j = 0, 1, ..., i - 1;

因为从虫洞j不可能回到虫洞i,所以j终点到i起点的方案数为0

ok,有了递推式,现在只要求解cnk模,《挑战》P294有关于CNK取模的算法,但这针对的是p不一定是素数的情况,而此题MOD = 1000000007,是个大素数,所以可以直接取fact的逆,公式为:

CKN≡a1(a2a3)−1

C_N^K \equiv a_1(a_2a_3)^{-1}

代码如下:

import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class Main{
    InputStream is;
    PrintWriter out;
    String INPUT = "./data/judge/201709/A2214.txt";

    static Scanner in;
    public static void main(String[] args) throws Exception {
        in = new Scanner(System.in);
        new Main().solve();
    }

    static final int MOD = 1000000007;

    class Wormhole implements Comparable<Wormhole> {
        int x1;
        int y1;
        int x2;
        int y2;

        public Wormhole(int x1, int y1, int x2, int y2) {
            this.x1 = x1;
            this.x2 = x2;
            this.y1 = y1;
            this.y2 = y2;
        }

        @Override
        public int compareTo(Wormhole that) {
            return this.x1 != that.x1 ? this.x1 - that.x1 : this.y1 - that.y1;
        }
    }

    List<Wormhole> holes;

    void solve() {

        init_factorial();

        while (true) {
            int n = in.nextInt();
            int m = in.nextInt();
            int k = in.nextInt();

            if (n == 0 && m == 0 && k == 0) break;
            holes = new ArrayList<>();
            n--;
            m--;
            for (int i = 0; i < k; ++i) {
                int x1 = in.nextInt();
                int y1 = in.nextInt();
                int x2 = in.nextInt();
                int y2 = in.nextInt();
                holes.add(new Wormhole(x1 - 1, y1 - 1, x2 - 1, y2 - 1));
            }

            Collections.sort(holes);
            holes.add(new Wormhole(n, m, n + 1, m + 1));

            int[] dp = new int[k + 16];
            for (int i = 0; i <= k; ++i) {
                Wormhole hole = holes.get(i);
                dp[i] = nck(hole.x1 + hole.y1, hole.x1);
                for (int j = 0; j < i; ++j) {
                    Wormhole prev = holes.get(j);
                    dp[i] = add(dp[i], mul(dp[j],
                            sub(calc(prev.x2, prev.y2, hole.x1, hole.y1), calc(prev.x1, prev.y1, hole.x1, hole.y1))));
                }
            }

            System.out.println(dp[k]);
        }
    }

    /************************* mod_fact && mod_comb *****************************/
    int add(int a, int b) {
        return (a + b) % MOD;
    }

    int sub(int a, int b) {
        return (a - b + MOD) % MOD;
    }

    int mul(long a, long b) {
        return (int) (((a * b)) % MOD);
    }

    int[] fact = new int[200010];

    void init_factorial() {
        fact[0] = 1;
        for (int i = 1; i < 200000; ++i) {
            fact[i] = mul(fact[i - 1], i);
        }
    }

    class GCD {
        int d;
        int x;
        int y;

        public GCD(int d, int x, int y) {
            this.d = d;
            this.x = x;
            this.y = y;
        }
    }

    GCD extgcd(int a, int b) {
        if (b == 0)
            return new GCD(a, 1, 0);
        else {
            GCD p = extgcd(b, a % b);
            GCD ans = new GCD(0, 0, 0);
            ans.d = p.d;
            ans.x = p.y;
            ans.y = p.x - (a / b) * p.y;
            return ans;
        }
    }

    int inv(int a, int m) {
        GCD p = extgcd(a, m);
        if (p.d != 1)
            return -1;
        return (p.x % m + m) % m;
    }

    int nck(int n, int k) {
        return mul(mul(fact[n], inv(fact[n - k], MOD)), inv(fact[k], MOD));
    }

    int calc(int x1, int y1, int x2, int y2) {
        if (x2 < x1 || y2 < y1)
            return 0;
        return nck(x2 - x1 + y2 - x1, x2 - x1);
    }
}

测试用例都过了,但放在AOJ上就RE,总感觉测试接口有问题。。。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年09月04日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 挑战程序竞赛系列(47):4.1 计数 二项式取模
    • AOJ 2214: Warp Hall
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档