@bzoj - [email protected] 礼物

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

一年一度的圣诞节快要来到了。每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小E心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多。
小E从商店中购买了n件礼物,打算送给m个人,其中送给第i个人礼物数量为wi。
请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某个人在这两种方案中收到的礼物不同)。
由于方案数可能会很大,你只需要输出模P后的结果。

Input
输入的第一行包含一个正整数P,表示模;
第二行包含两个整整数n和m,分别表示小E从商店购买的礼物数和接受礼物的人数;
以下m行每行仅包含一个正整数wi,表示小E要送给第i个人的礼物数量。

Output
若不存在可行方案,则输出“Impossible”,否则输出一个整数,表示模P后的方案数。

Sample Input
100
4 2
1
2

Sample Output
12

【样例说明】
下面是对样例1的说明。
以“/”分割,“/”前后分别表示送给第一个人和第二个人的礼物编号。12种方案详情如下:
1/23 1/24 1/34
2/13 2/14 2/34
3/12 3/14 3/24
4/12 4/13 4/23

【数据规模和约定】
设P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
对于100%的数据,1≤n≤10^9,1≤m≤5,1≤pi^ci≤10^5。

@[email protected]

令 sum = 所有的 wi 之和,则答案 ans 的表达式如下:
\[\frac{n!}{w_1!*w_2!*...*w_m!*(n-sum)!}\]

当 sum > n 时无解。问题转换为求上式对 P 取模的值。
此时我们需要扩展 lucas 定理(因为 P 不一定是质数所以要扩展的)。
(mark 一下yhn学长的博客)。

首先可以将 P 唯一分解一下,得到 P = p1^c1 * p2^c2 * p3^c3 * … * pt^ct。然后分开求再中国剩余定理合并(中国剩余定理自行百度可以学)。
那么实际上我们只需要考虑模数为质数的幂的情况,不妨设模数为 p^c。
我们通过扩展 lucas 将阶乘 n! 拆写成 A * p^B 的形式,其中 A 与 p 互质。这样的形式下做阶乘之间的除法,只需要 A 部分直接逆元,B 部分直接减即可。

怎么才能将阶乘 n! 拆写成 A * p^B 的形式呢?
先将 \(n! = 1*2*...*n\) 写下,然后将其中 p 的倍数共同提出一个 p 因子来,得到 \(n! = (n/p)!*p^{n/p}*1*2*...*(p-1)*(p+1)*...\)。
注意到此时整个式子可以分成三部分:\((n/p)!\) 这一部分递归求解;\(p^{n/p}\) 这一部分融入上面的 B;\(1*2*...*(p-1)*(p+1)*...\) 这一部分融入上面的 A。
但是第三部分还是不好求。继续观察发现第三部分是有循环节的(因为是在模 p^c 的意义下的),求出循环节的个数、循环节内的乘积、剩余部分的乘积即可。

或许有人认为 lucas 定理只适用于组合数,但通过上文我们可以发现,扩展意义下的 lucas 定理可以用于模意义下阶乘之间的乘除。
也许改天可以用这个性质出个什么题。

@accepted [email protected]

#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
const int MAXN = 100000;
typedef pair<int, int> pii;
int pow_mod(int b, int p, int m) {
    int ret = 1;
    while( p ) {
        if( p & 1 ) ret = 1LL*ret*b%m;
        b = 1LL*b*b%m;
        p >>= 1;
    }
    return ret;
}
vector<int>vec;
int fct[MAXN + 5];
pii lucas(int x, int b, int m) {
    if( x == 0 ) return make_pair(1, 0);
    pii tmp = lucas(x/b, b, m);
    return make_pair(1LL*tmp.first*pow_mod(fct[m-1], x/m, m)%m*fct[x%m]%m, tmp.second+x/b);
}
int P, n, m;
int main() {
    scanf("%d%d%d", &P, &n, &m);
    int tmp = P, sum = 0, ans = 0;
    for(int i=1;i<=m;i++) {
        int x; scanf("%d", &x);
        vec.push_back(x), sum += x;
    }
    if( sum > n ) {
        puts("Impossible");
        return 0;
    }
    vec.push_back(n-sum);
    for(int i=2;i<=MAXN&&i<=tmp;i++)
        if( tmp % i == 0 ) {
            int m = 1;
            while( tmp % i == 0 )
                tmp /= i, m *= i;
            fct[0] = 1;
            for(int j=1;j<m;j++) {
                fct[j] = fct[j-1];
                if( j % i ) fct[j] = 1LL*fct[j]*j%m;
            }
            pii res = lucas(n, i, m);
            for(int j=0;j<vec.size();j++) {
                pii tmp2 = lucas(vec[j], i, m);
                res.first = 1LL*res.first*pow_mod(tmp2.first, m - m/i - 1, m)%m;
                res.second -= tmp2.second;
            }
            int del = 1LL*res.first*pow_mod(i, res.second, m)%m;
            ans = (ans + 1LL*(P/m)*pow_mod(P/m, m - m/i - 1, m)%P*del%P)%P;
        }
    printf("%d\n", ans);
}

@[email protected]

题目中真的没有 P 的范围。。。找了很久都没找到。。。

看到网上的题解大多 P 用的是 long long,还以为我开 int 过不了。
结果其实过得了的。。。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11147882.html

时间: 2024-10-03 15:14:01

@bzoj - [email protected] 礼物的相关文章

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好

@bzoj - [email&#160;protected] [Poi2011]Lightning Conductor

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @version - [email protected] @version - [email protected] @[email protected] @[email protected] 已知一个长度为

@bzoj - [email&#160;protected] [POI2014]Hotel加强版

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵树,求无序三元组 (a, b, c) 的个数,使得 dis(a, b) = dis(b, c) = dis(c, a),且 a ≠ b, b ≠ c, c ≠ a. inpu

@bzoj - [email&#160;protected] 旅行规划

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 请你维护一个序列,支持两种操作: (1)某个区间 [x, y] 内的数同时加上一个增量 k. (2)询问某一个区间 [x, y] 中从 1 开始的最大前缀和. input 第一行给出一个整数 n.n <= 100000.接下来一行 n 个整数表示序列的初始值. 第三行给出一个整数 m,

@bzoj - [email&#160;protected] Hard Nim

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 堆石子,每堆石子的数量是不超过 m 的一个质数. 两个人玩 nim 游戏,问使后手必胜的初始局面有多少种. 模 10^9 + 7. input 多组数据.数据组数 <= 80. 每组数据一行两个正整数,n 和 m.1 <= n <= 10^9, 2 <= m <

@bzoj - [email&#160;protected] [Cqoi2016]伪光滑数

目录 @description@ @[email protected] @version - [email protected] @version - [email protected] @accepted [email protected] @[email protected] @description@ 若一个大于 1 的整数 M 的质因数分解有 k 项,其最大的质因子为 \(A_k\),并且满足 \(A_k^k \le N\),\(A_k < 128\),我们就称整数 M 为 N - 伪光

@bzoj - [email&#160;protected] [POI2015] Wilcze do?y

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0. 请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p . input 第一行包含三个整数 n, p, d (1 <= d <= n <= 2000000,0 &

@bzoj - [email&#160;protected] [POI2015] Pustynia

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的正整数序列 a,每个数都在 1 到 10^9 范围内. 告诉你其中 s 个数,并给出 m 条信息,每条信息包含三个数 l, r, k 以及 k 个正整数,表示 a[l], a[l+1], ..., a[r-1], a[r] 里这 k 个数中的任意一个都比任意一个剩

@bzoj - [email&#160;protected] [POI2015] Logistyka

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作: 1.U k a 将序列中第 k 个数修改为 a. 2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作. 每次询问独立,即每次询问不会对序列进行修改. input 第一行包含两个