zoukankan      html  css  js  c++  java
  • [题解] ATCODER ABC172E NEQ

    前言

    来篇 atcoder 的题解欧~

    题目链接

    题意

    有两个包含 (n) 个数字的序列 (A)(B) ,满足一下条件:

    • (1leq A_i,B_ileq m,(iin[i,n]))
    • (A_i eq B_i,(iin[i,n]))
    • (A_i eq A_j,B_i eq B_j,(1leq i<jleq n))

    给定 (n)(m) ,且 (nleq m) ,求合法的方案数,答案需要对 (1e9+7) 取模。

    两种不同的方案,当且仅当序列 (A) 不同或序列 (B) 不同。

    两个序列不同,当且仅当 (forall i,jleq n,a[i] eq a[j])

    思路

    错排问题的变式。

    首先来说明错排问题的递推解法:(dp[i]=(i-1)(dp[i-1]+dp[i-2]))

    其中, (dp[i])(1) ~ (i) 的错排方案数。

    证明:对于第 (i) 个加入的数字,值为 (i) ,有 (n-1) 种放发(不能够放在位置 (n) 上)。

    对于每一个 (k) 不与 (i) 相等,将 (i) 放在位置 (k) ,有两种情况。

    • (k) 放在位置 (i) 上,则相当于不管 (i)(k) ,然后剩下的 (i-2) 个数字错排,有 (dp[i-2]) 种情况。
    • (k) 没有放在位置 (i) 上,因为 (k) 不能放在位置 (i) 上,所以就相当于不包括数字 (i) 的错排,有 (dp[i-1]) 种情况。

    我们首先固定序列 (A) ,选出 (n) 个数字,则共有 (A_m^n) 种方案。则对于这 (A_m^n) 种方案,所选出的 (B) 构成的合法方案都不同,那么就只用针对一个典型的案例来进行研究就行了。为了方便,选择序列 (A={1,2dots n}) 为典例。

    则问题就可以转换为:加入 (n) 个数字使得这些数字都在 (1) ~ (m) 内,且互不相同,且满足:(i) 不在位置 (i) 上,(iin[1,n])

    我将这个问题称之为“假错排”。

    “假错排”就是在上述的情况上加上一种情况:

    • 对于第 (i) 次加入数字,将任意一个数字 (k(kin[1,m],k otinlbrace b_1,b_2dots b_{i-1} brace,k otin[i+1,n])) 填入位置 (n) ,则可以选择 (m-n) 种数字。

    对于上面的这句话可以理解为最开始可以选择不在 (A) 序列中的数字,有 (m-n) 个数字,这些数字是不参与 (i-1) 前的“假错排”的。而每选择一个这样的数字,都会覆盖掉一个位置 (k) ,也就是剩下了 (m-n-1) 个数字。但是,数字 (k) 在之后的筛选中,将不会参与 (i-1) 前的“假错排”,所以有每次都有 (m-n) 个数字可供选择,即第三种情况。

    则可以得到状态转移方程:(dp[i]=(m-n)dp[i-1]+(i-1)(dp[i-1]+dp[i-2]))

    然后这道题就做完了。

    Code

    时间复杂度为 (O(n)) ,代码很短。

    #include <cstdio>
    #define int long long
    const int MAXN = 5e5 + 5;
    const int MOD = 1e9 + 7;
    int dp[MAXN], n, m, ans;
    signed main() {
    	scanf("%lld %lld", &n, &m);
    	dp[0] = 1;
    	dp[1] = m - n;
    	for(int i = 2; i <= n; i++)
    		dp[i] = ((m - n) * dp[i - 1] % MOD + (i - 1) * (dp[i - 2] + dp[i - 1]) % MOD) % MOD;
    	ans = dp[n];
    	for(int i = m, j = 1; j <= n; i--, j++)
    		ans = (ans * i) % MOD;
    	printf("%lld", ans);
    	return 0;
    }
    
  • 相关阅读:
    Reactor系列(四)subscribe订阅
    Reactor系列(三)创建Flux,Mono(续)
    Reactor系列(二)Flux Mono创建
    Reactor系列(一)基本概念
    Stream系列(十五)File方法使用
    Stream系列(十四)parallet方法使用
    OpenCV二值化、归一化操作
    C# 队列
    linux shell脚本程序路径作为变量
    C++中头文件(.h)和源文件(.cpp)都应该写些什么
  • 原文地址:https://www.cnblogs.com/C202202chenkelin/p/15008669.html
Copyright © 2011-2022 走看看