P3301 [SDOI2013]方程
题意:
题解:
插板法介绍
首先要先讲组合数学的一个方法:插板法
问题引出:把10个球放进三个盒子,每个箱子至少一个有多少种分法?
10个球就有9个空隙,我们可以考虑在这个9个空隙中放入两个隔板,这样10个球就被分成了3组,就相当于放入了三个箱子。
答案就是C10−13−1C_{10-1}^{3-1}C10−13−1
也就是n个球放入m个盒子,每个箱子至少一个有Cn−1m−1C_{n-1}^{m-1}Cn−1m−1种分法
问题2:把10个球放进三个盒子有多少种分法
此时箱子内可以没有球,我们可以预先在3个盒子里都放一个球,则问题转化为将13个球放进3个盒子里,每个盒子至少一个有多少中放法。答案为C122C_{12}^2C122
也就是n个球放入m个盒子,有Cn+m−1m−1C_{n+m-1}^{m-1}Cn+m−1m−1种分法
本题讲解:
如果没有限制,就是求x1+x2+..+xn=Mx_{1}+x_{2}+..+x_{n}=Mx1+x2+..+xn=M,这不就相当于把M分配到n个箱子里,且每个箱子不能为空,这样答案就是CM−1n−1C_{M-1}^{n-1}CM−1n−1
但是题目有两类限制:
第二类限制为:Xi>=AiX_{i}>=A_{i}Xi>=Ai,我们可以巧妙的转化,对于第i个箱子要求分配数要大于AiA_{i}Ai,那我们可以认为先将Ai−1A_{i}-1Ai−1分配给XiX_{i}Xi,然后剩下还是老分法(分配给n个箱子,每个箱子至少一个)。这种方法M要减去Ai−1A_{i}-1Ai−1(相当于提前分配了)
对于第一类限制,Xi<=AiX_{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∗1007262203414=2*3*11*397*1007262203414=2∗3∗11∗397∗1007
437367875=53∗73∗1012437367875=5^3*7^3*101^2437367875=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 LOCALstartTime= clock();freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef LOCALendTime= 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=1;i<=cnt;i++){retfac[i]=1;for(int j=1;j<=qk[i];j++){if(j%qw[i]){retfac[i] = 1ll * retfac[i] * j % qk[i];}}}
}ll t;
const int maxn= 10;
int a[maxn], b[maxn];
ll n, n1, n2, m;
void init(){}
int main()
{//rd_test();read(t, mod);init(mod);//预处理,不然会t三个点 while (t--) {read(n, n1, n2, m);for (int i= 1; i <= n1; i++)read(a[i]);for (int i= 1; i <= n2; i++) {read(b[i]);m-= (b[i] - 1);}//ll sum=exLucas(m-1,n-1,mod);ll ans= 0;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;}printf("%lld\n", ans);}return 0;//Time_test();
}