[BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】
题目链接:BZOJ - 3129
题目分析
使用隔板法的思想,如果没有任何限制条件,那么方案数就是 C(m - 1, n - 1)。
如果有一个限制条件是 xi >= Ai ,那么我们就可以将 m 减去 Ai - 1 ,相当于将这一部分固定分给 xi,就转化为无限制的情况了。
如果有一些限制条件是 xi <= Ai 呢?直接来求就不行了,但是注意到这样的限制不超过 8 个,我们可以使用容斥原理来求。
考虑容斥:考虑哪些限制条件被违反了,也就是说,有哪些限制为 xi <= Ai 却是 xi > Ai,这样就转化为了 xi >= Ai 的限制条件。
那么我们就可以在 2^8 * T(求组合数) 的时间内求出答案了。
怎么求这个组合数呢?直接预处理阶乘的逆元是不可以的,因为模数不都是质数。
我们要将模数拆成一个个 pi^ai 这样的形式,使得它们两两之间互质,就可以分别求出答案,最后再用中国剩余定理组合起来。
中国剩余定理:如果有n个方程 x = xi (mod mi) ,M = m1 * m2 * .. * mn ,那么在 mod M 的意义下,方程组有一个唯一解。
x = sigma(Mi * Inv(Mi) * xi) % M ,其中 Mi = M / mi ,Inv(Mi)是Mi在mod mi意义下的逆元。
那么我们的问题就是,如何求出 C(n, m) % (p^a) 。
这里就需要用到“组合数取模”了,专门用来求解这种问题。
使用类似于快速阶乘的方法,将组合数中分数线上下的阶乘都拆成 e * p^f 的形式,然后 e 直接计算,f 分数线上下相减之后再计算。
怎么将 x! 拆成 e * p^f 呢?
假设我们要 mod 的数是 p^a ,那么我们需要预处理出 [1, p^a - 1] 中除去 p 的倍数的其余数的前缀积(类似阶乘少了 p 的倍数)。
然后我们知道 [1, x] 中包含 p 的数有 x / p 个,我们将这些数中都提取出 1 个 p,那么就获得了 p^(x/p),然后这 x / p 个数就变成了 [1, x/p],就可以递归下去。
其余的部分可以分段来求,分成 [1, p^a - 1], [p^a + 1, p^a + p^a - 1] ..... 这样,每一段的积都是一样的 (mod p^a 意义下),直接快速幂就可以了。
最后还会剩下一段 [1, x % (p^a)] ,也是直接预处理出的值。
这样这道题就做完了(呼~)。
另外注意的是,在写代码的时候,我求逆元使用欧拉定理但是确用错了。
欧拉定理:a^phi(b) = 1 (mod b) 条件:gcd(a, b) = 1
注意是 a^phi(b) 而不是 a^(b-1) !当 b 不是质数的时候就跪了!
代码
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio> using namespace std; typedef long long LL;
typedef double LF; const int MaxP = 10201 + 15, MaxN1 = 8 + 5; int T, p, n, n1, n2, m, Top, Ans;
int A[MaxN1]; LL Temp;
LL Fac[10][MaxP], Pr[10], Pi[10], Pa[10], Phi_Pi[10], Mi[10], Inv_Mi[10], Xi[10]; LL Pow(LL a, LL b, LL Mod)
{
LL ret, f;
ret = 1; f = a;
while (b)
{
if (b & 1)
{
ret *= f;
ret %= Mod;
}
b >>= 1;
f *= f;
f %= Mod;
}
return ret;
} void Prepare()
{
int x, SqrtX;
x = p; SqrtX = (int)sqrt((LF)x);
Top = 0;
for (int i = 2; i <= SqrtX; ++i)
{
if (x % i != 0) continue;
Pr[++Top] = i;
Pa[Top] = 0;
Pi[Top] = 1;
while (x % i == 0)
{
++Pa[Top];
Pi[Top] *= i;
x /= i;
}
Phi_Pi[Top] = Pi[Top] / Pr[Top] * (Pr[Top] - 1);
}
if (x > 1)
{
Pr[++Top] = x;
Pa[Top] = 1;
Pi[Top] = x;
Phi_Pi[Top] = Pi[Top] - 1;
}
for (int i = 1; i <= Top; ++i)
{
Mi[i] = p / Pi[i];
Inv_Mi[i] = Pow(Mi[i], Phi_Pi[i] - 1, Pi[i]);
Fac[i][0] = 1;
for (int j = 1; j < Pi[i]; ++j)
{
if (j % Pr[i] != 0) Fac[i][j] = Fac[i][j - 1] * j % Pi[i];
else Fac[i][j] = Fac[i][j - 1];
}
}
} struct ES
{
LL e, f;
}; ES Calc(int x, int k)
{
ES ret, tc;
if (x < Pr[k])
{
ret.e = Fac[k][x];
ret.f = 0;
return ret;
}
ret.f = x / Pr[k];
tc = Calc(x / Pr[k], k);
ret.f += tc.f;
ret.e = tc.e * Fac[k][x % Pi[k]] % Pi[k];
ret.e = ret.e * Pow(Fac[k][Pi[k] - 1], x / Pi[k], Pi[k]) % Pi[k];
return ret;
} LL C(int x, int y, int k)
{
LL ret;
int pf;
ES Ex, Ey, Exy;
Ex = Calc(x, k);
Ey = Calc(y, k);
Exy = Calc(x - y, k);
ret = Ex.e * Pow(Ey.e, Phi_Pi[k] - 1, Pi[k]) % Pi[k] * Pow(Exy.e, Phi_Pi[k] - 1, Pi[k]) % Pi[k];
pf = Ex.f - Ey.f - Exy.f;
if (pf >= Pa[k]) ret = 0;
else ret = ret * Pow(Pr[k], pf, Pi[k]) % Pi[k];
return ret;
} int C(int x, int y)
{
if (x == y) return 1;
if (x < y) return 0;
if (y == 0) return 1;
LL ret = 0;
for (int i = 1; i <= Top; ++i) Xi[i] = C(x, y, i);
for (int i = 1; i <= Top; ++i)
{
ret += Xi[i] * Mi[i] % p * Inv_Mi[i] % p;
ret %= p;
}
return (int)ret;
} void DFS(int x, int Cnt, int Sum)
{
if (x == n1)
{
if (Cnt & 1) Temp -= C(m - Sum - 1, n - 1);
else Temp += C(m - Sum - 1, n - 1);
Temp = (Temp % p + p) % p;
return;
}
DFS(x + 1, Cnt, Sum);
DFS(x + 1, Cnt + 1, Sum + A[x + 1]);
} int Solve()
{
Temp = 0;
DFS(1, 0, 0);
DFS(1, 1, A[1]);
return (int)Temp;
} int main()
{
scanf("%d%d", &T, &p);
Prepare();
for (int Case = 1; Case <= T; ++Case)
{
scanf("%d%d%d%d", &n, &n1, &n2, &m);
for (int i = 1; i <= n1; ++i) scanf("%d", &A[i]);
int Num;
for (int i = 1; i <= n2; ++i)
{
scanf("%d", &Num);
m -= Num - 1;
}
if (n1 > 0) Ans = Solve();
else Ans = C(m - 1, n - 1);
printf("%d\n", Ans);
}
return 0;
}
[BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】的更多相关文章
- ●BZOJ 3129 [Sdoi2013]方程
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3129 题解: 容斥,扩展Lucas,中国剩余定理 先看看不管限制,只需要每个位置都是正整数时 ...
- BZOJ 3129 [SDOI2013]方程 (拓展Lucas)
题目大意:给定一个方程$X_{1}+X_{2}+X_{3}+X_{4}+...+X_{n}=M$,$\forall X_{i}<=A_{i} (i<=n1)$ $\forall X_{i} ...
- BZOJ 3129 SDOI2013 方程
如果没有限制,答案直接用隔板法C(m-1,n-1) 对于>=x的限制,我们直接在对应位置先放上x-1即可,即m=m-(x-1) 对于<=x的限制,由于限制很小我们可以利用容斥原理将它转化为 ...
- bzoj 3202 [Sdoi2013]项链——容斥+置换+推式子
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3202 可见Zinn博客:https://www.cnblogs.com/Zinn/p/100 ...
- 组合数取模Lucas定理及快速幂取模
组合数取模就是求的值,根据,和的取值范围不同,采取的方法也不一样. 下面,我们来看常见的两种取值情况(m.n在64位整数型范围内) (1) , 此时较简单,在O(n2)可承受的情况下组合数的计算可以 ...
- 排列组合+组合数取模 HDU 5894
// 排列组合+组合数取模 HDU 5894 // 题意:n个座位不同,m个人去坐(人是一样的),每个人之间至少相隔k个座位问方案数 // 思路: // 定好m个人 相邻人之间k个座位 剩下就剩n-( ...
- hdu 3944 DP? 组合数取模(Lucas定理+预处理+帕斯卡公式优化)
DP? Problem Description Figure 1 shows the Yang Hui Triangle. We number the row from top to bottom 0 ...
- lucas定理解决大组合数取模
LL MyPow(LL a, LL b) { LL ret = ; while (b) { ) ret = ret * a % MOD; a = a * a % MOD; b >>= ; ...
- 2015 ICL, Finals, Div. 1 Ceizenpok’s formula(组合数取模,扩展lucas定理)
J. Ceizenpok’s formula time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
随机推荐
- 使用 JSSE 定制 SSL 连接的属性--转载
当数据在网络上传播的时候,通过使用 SSL 对其进行加密和保护,JSSE 为 Java 应用程序提供了安全的通信.在本篇有关该技术的高级研究中,Java 中间件开发人员 Ian Parkinson 深 ...
- Getting started with new I/O (NIO)--reference
The new input/output (NIO) library, introduced with JDK 1.4, provides high-speed, block-oriented I/O ...
- vi 快捷键
屏幕翻滚类命令Ctrl+u:向文件首翻半屏Ctrl+d:向文件尾翻半屏Ctrl+f:向文件尾翻一屏Ctrl+b:向文件首翻一屏 shift+g:到尾部 H:跳到第一行.M:跳到中间.L:跳到最后一行. ...
- ubuntu文件管理常用命令 分类: linux ubuntu 学习笔记 2015-07-02 16:57 29人阅读 评论(0) 收藏
1.关闭防火墙:ufw disable 2.以.开头的表示隐藏文件 3..和..分别代表当前目录以及当前目录的父目录 4.显示当前用户所在目录pwd 5.touch创建空文件 6.mkdir创建新目录 ...
- VC/MFC 下 递归遍历目录下的所有子目录及文件
在MFC下要实现文件夹的递归遍历,可用CFileFind类,依次读取文件夹下的子文件夹和文件,并判断通过判断是文件夹还是文件来决定递归遍历.递归遍历代码如下: /******************* ...
- VC++ 获取windows系统的版本类型
vc中获取windows版本信息,一般是调用GetVersionEx 这个API函数来获取的,这个API需要OSVERSIONINFOEX 这个结构体作为参数,OSVERSIONINFOEX 的对应的 ...
- loading图片制作(没有设计师的情况下,前端同学自己制作loading动图)
svg css gif http://loading.io/
- Java 之 MYSQL 数据库搭建
1.首先要去加载java的mysql驱动,将下载的mysql-connect-bin-java.jar包添加到该项目下2.然后通过 Class.forName("com.mysql.jdbc ...
- sqlserver 自定义字符串分割函数.
--SQL Server Split函数 --Author: sq --说明:被分割后的字段为:short_str --支持分割符多字节 --使用方法 --Select * FROM splits(' ...
- 多线程与Socket编程
一.死锁 定义: 指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁 ...