P3301 [SDOI2013]方程
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3301 [SDOI2013]方程相关的知识,希望对你有一定的参考价值。
题意:
题解:
插板法介绍
首先要先讲组合数学的一个方法:插板法
问题引出:把10个球放进三个盒子,每个箱子至少一个有多少种分法?
10个球就有9个空隙,我们可以考虑在这个9个空隙中放入两个隔板,这样10个球就被分成了3组,就相当于放入了三个箱子。
答案就是
C
10
−
1
3
−
1
C_{10-1}^{3-1}
C10−13−1
也就是n个球放入m个盒子,每个箱子至少一个有
C
n
−
1
m
−
1
C_{n-1}^{m-1}
Cn−1m−1种分法
问题2:把10个球放进三个盒子有多少种分法
此时箱子内可以没有球,我们可以预先在3个盒子里都放一个球,则问题转化为将13个球放进3个盒子里,每个盒子至少一个有多少中放法。答案为
C
12
2
C_{12}^2
C122
也就是n个球放入m个盒子,有
C
n
+
m
−
1
m
−
1
C_{n+m-1}^{m-1}
Cn+m−1m−1种分法
本题讲解:
如果没有限制,就是求
x
1
+
x
2
+
.
.
+
x
n
=
M
x_{1}+x_{2}+..+x_{n}=M
x1+x2+..+xn=M,这不就相当于把M分配到n个箱子里,且每个箱子不能为空,这样答案就是
C
M
−
1
n
−
1
C_{M-1}^{n-1}
CM−1n−1
但是题目有两类限制:
第二类限制为: X i > = A i X_{i}>=A_{i} Xi>=Ai,我们可以巧妙的转化,对于第i个箱子要求分配数要大于 A i A_{i} Ai,那我们可以认为先将 A i − 1 A_{i}-1 Ai−1分配给 X i X_{i} Xi,然后剩下还是老分法(分配给n个箱子,每个箱子至少一个)。这种方法M要减去 A i − 1 A_{i}-1 Ai−1(相当于提前分配了)
对于第一类限制,
X
i
<
=
A
i
X_{i}<=A_{i}
Xi<=Ai,就比较麻烦了,不过题目中限制数最大为8,我们可以用容斥解决
先计算不考虑前n1个数的限制方案数-前n1个数至少有一个不满足条件的方案数+前n1个数至少有2个不满足条件的方案数-…
代码具体就是枚举状态S,其二进制状态下,第i位为1表示计算了第i个数,如果有奇数个1就是减,偶数个1就是加
因为n,m很大,且p不一定为质数,所以用扩展卢卡斯
容斥代码:
for (int s= 0; s <= (1 << n1) - 1; s++) {
int num= 0;
ll now= m;
for (int i= 1; i <= n1; i++) {
if ((1 << (i - 1)) & s) {
now-= a[i];
num++;
}
}
ll tmp= exLucas(now - 1, n - 1, mod);
// printf("tmp=%lld\\n",tmp);
ans= (ans + ((num & 1) ? (mod - tmp) % mod : tmp)) % mod;
}
这样做70分,会被卡常
题目所给的模数只有三种:
262203414
=
2
∗
3
∗
11
∗
397
∗
1007
262203414=2*3*11*397*1007
262203414=2∗3∗11∗397∗1007
437367875
=
5
3
∗
7
3
∗
10
1
2
437367875=5^3*7^3*101^2
437367875=53∗73∗1012
10007
是
质
数
10007是质数
10007是质数
这样可以省去扩展卢卡斯分析qk的过程,大大减少常数
同时优化一下扩展卢卡斯处理阶乘的过程,提前计算好,这样也可以大力卡常。不然根本卡不过去。
代码:
// Problem: P3301 [SDOI2013]方程
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3301
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Data:2021-08-27 16:25:48
// By Jozky
#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{
x= 0;
char c= getchar();
bool flag= 0;
while (c < '0' || c > '9')
flag|= (c == '-'), c= getchar();
while (c >= '0' && c <= '9')
x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();
if (flag)
x= -x;
read(Ar...);
}
template <typename T> inline void write(T x)
{
if (x < 0) {
x= ~(x - 1);
putchar('-');
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef LOCAL
startTime= clock();
freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef LOCAL
endTime= clock();
printf("\\nRun Time:%lfs\\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
ll mod;
void exgcd(ll a, ll b, ll& x, ll& y)
{
if (!b) {
x= 1, y= 0;
return;
}
exgcd(b, a % b, y, x);
y-= a / b * x;
return;
}
inline ll inv(ll n, ll p)
{
ll x, y;
exgcd(n, p, x, y);
return (x + p) % p;
}
ll qpow(ll base, ll p, ll mod)
{
ll ret= 1;
for (; p; p>>= 1, base= base * base % mod)
if (p & 1)
ret= ret * base % mod;
return ret;
}
ll CRT(int n, ll* a, ll* m)
{
ll M= 1, ret= 0;
for (ll i= 1; i <= n; i++)
M*= m[i];
for (ll i= 1; i <= n; i++) {
ll w= M / m[i];
ret= (ret + a[i] * w % mod * inv(w, m[i]) % mod) % mod;
}
return (ret + mod) % mod;
}
int retfac[10];
ll calc(ll n, ll q, ll qk,ll retfac)
{
if (!n)
return 1;
ll ret= retfac;
ret= qpow(ret, n / qk, qk);
for (ll i= n / qk * qk + 1; i <= n; i++)
if (i % q)
ret= ret * (i % qk) % qk;
return ret * calc(n / q, q, qk,retfac) % qk;
}
ll multiLucas(ll n, ll m, ll q, ll qk,ll retfac)
{
int cnt= 0;
for (ll i= n; i; i/= q)
cnt+= i / q;
for (ll i= m; i; i/= q)
cnt-= i / q;
for (ll i= n - m; i; i/= q)
cnt-= i / q;
return qpow(q, cnt, qk) * calc(n, q, qk,retfac) % qk * inv(calc(m, q, qk,retfac), qk) % qk * inv(calc(n - m, q, qk,retfac), qk) % qk;
}
int cnt;
ll qk[20];
ll qw[20];
ll exLucas(ll n, ll m, ll p)
{
if(m>n)return 0;
ll a[20];
// int cnt= 0;
// ll qk[20], a[20]; //存放所有的 q^k 和待合并答案的结果
//
// for (ll i= 2; i * i <= p; ++i) //质因数分解
// {
// if (p % i == 0) {
// qk[++cnt]= 1;
// while (p % i == 0)
// qk[cnt]*= i, p/= i;
// a[cnt]= multiLucas(n, m, i, qk[cnt]);
// }
// }
// if (p > 1)
// qk[++cnt]= p, a[cnt]= multiLucas(n, m, p, p);
for(int i=1;i<=cnt;i++){
a[i]=multiLucas(n, m,qw[i] , qk[i],retfac[i]);
}
return CRT(cnt, a, qk); //CRT 合并答案
}
void init(ll p) {
for (ll i= 2; i * i <= p; ++i) //质因数分解
{
if (p % i == 0) {
qk[++cnt]= 1;
qw[cnt]=i;
while (p % i == 0)
qk[cnt]*= i, p/= i;
}
}
if (p > 1)
qk[++cnt]= p,qw[cnt]=p;
for(int i=1bzoj3129[Sdoi2013]方程 exlucas+容斥原理