[SDOI2013]方程
嘟嘟嘟
这题思路还是非常显然的,烦人的就是模数不是质数。
只考虑限制2,就是插板法,先强制让后面的未知数选那么多球。
加上了限制1,因为\(n1 \leqslant 8\),那直接\(O(2 ^ 8)\)暴力容斥即可。
细节就在于解必须是正整数,即每一个盒子至少放一个球,那么对于限制2,我们要强制先选\(a _ i - 1\)个球,而不是\(a_i\)个;同理限制1,容斥的时候至少超出限制的数得选\(a_i\)个球,而不是\(a_i + 1\)个。
至此这道题已经全部搞完了。还剩下一个大问题:模数不是质数。
把模数质因数分解,发现最大的质因数也很小,因此要用扩展lucas。
推荐一个很好的教程博客:【知识总结】扩展卢卡斯定理(exLucas)。
不过这个博客的代码效率不是很高,这道题会TLE两个点。然后我找了另一种写法,就是在求\(n! \% p ^ k\)的时候,我们可以先预处理前缀积\(f[i](i < p ^ k)\),满足如果\(i | p\),则\(f[i] = f[i - 1]\),否则\(f[i] = f[i - 1] * i \% p ^ k\)。
这样在递归的每一层,原先从1到\(p ^ k\)和从\(1\)到\(n \% p ^ k\)分别扫一遍计算就改成\(O(1)\)的了,快的飞起。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<assert.h>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 20;
In ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
In void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
freopen("ha.in", "r", stdin);
freopen("ha.out", "w", stdout);
#endif
}
ll n, n1, n2, m, mod, a[maxn];
In ll inc(ll a, ll b, ll mod) {return a + b < mod ? a + b : a + b - mod;}
In ll quickpow(ll a, ll b, ll mod)
{
ll ret = 1;
for(; b; b >>= 1, a = a * a % mod)
if(b & 1) ret = ret * a % mod;
return ret;
}
In void exgcd(ll a, ll b, ll& x, ll& y)
{
if(!b) x = 1, y = 0;
else exgcd(b, a % b, y, x), y -= a / b * x;
}
In ll inv(int a, ll p)
{
ll x, y;
exgcd(a, p, x, y);
return (x % p + p) % p;
}
const int maxN = 1e6 + 5;
struct ExL
{
ll f[maxN];
In ll Fac(ll n, ll p, ll pk)
{
if(n < p) return f[n];
return quickpow(f[pk - 1], n / pk, pk) * f[n % pk] % pk * Fac(n / p, p, pk) % pk;
}
In ll C(ll n, ll m, ll p, ll pk)
{
if(m > n) return 0;
f[0] = 1;
for(int i = 1; i < pk; ++i)
if(i % p) f[i] = f[i - 1] * i % pk;
else f[i] = f[i - 1];
ll f1 = Fac(n, p, pk), f2 = Fac(m, p, pk), f3 = Fac(n - m, p, pk), cnt = 0;
for(ll i = n; i; i /= p) cnt += i / p;
for(ll i = m; i; i /= p) cnt -= i / p;
for(ll i = n - m; i; i /= p) cnt -= i / p;
return f1 * inv(f2, pk) % pk * inv(f3, pk) % pk * quickpow(p, cnt, pk) % pk;
}
ll a[maxN], c[maxN], d[maxN], cnt;
In ll CRT()
{
ll M = 1, ans = 0;
for(int i = 1; i <= cnt; ++i) M *= c[i];
for(int i = 1; i <= cnt; ++i)
ans = inc(ans, a[i] * (M / c[i]) % M * inv(M / c[i], c[i]) % M, M);
return ans;
}
In ll exlucas(ll n, ll m, ll p)
{
if(m > n) return 0;
for(int i = 1; i <= cnt; ++i) a[i] = C(n, m, d[i], c[i]);
return CRT();
}
In void init(ll mod)
{
if(mod == 10007)
{
cnt = 1;
d[1] = c[1] = 10007;
}
if(mod == 262203414)
{
cnt = 5;
d[1] = c[1] = 2, d[2] = c[2] = 3, d[3] = c[3] = 11;
d[4] = c[4] = 397, d[5] = c[5] = 10007;
}
if(mod == 437367875)
{
cnt = 3;
d[1] = 5, d[2] = 7, d[3] = 101;
c[1] = 125, c[2] = 343, c[3] = 10201;
}
}
}E;
int main()
{
MYFILE();
db Beg = clock();
int T = read(); mod = read();
E.init(mod);
while(T--)
{
n = read(), n1 = read(), n2 = read(), m = read();
for(int i = 1; i <= n1 + n2; ++i) a[i] = read();
ll ans = 0, sum2 = 0;
for(int i = n1 + 1; i <= n1 + n2; ++i) sum2 += a[i] - 1;
for(int S = 0; S < (1 << n1); ++S)
{
ll sum1 = 0, cnt = 0;
for(int i = 1; i <= n1; ++i)
if((S >> (i - 1)) & 1) sum1 += a[i], ++cnt;
ll tp = E.exlucas(m - 1 - sum1 - sum2, n - 1, mod);
ans = inc(ans, (cnt & 1) ? mod - tp : tp, mod);
}
write(ans), enter;
}
db End = clock();
//printf("%.3lf\n", (End - Beg) / CLOCKS_PER_SEC);
return 0;
}