扩展lucas+容斥

扩展lucas+容斥
给出一个方程x1+x2+x3+………xn=m
现在给出两种制约。
制约1:对于前解的前N1个数字,有制约xi<=a[i];
制约2:对于从N1+1到N1+2的数字,有制约xi>=a[i]
思路:对于制约2,比较好处理,在等式两边同时减去a[i]-1,使得有xi>=1,那么问题转化成了m-c个小球放进n个盒子的问题。然后对于制约1,我们想是不是也能通过一定的转化变成操作2,答案是肯定可以的,我们xi<=ai取对立面也就是xi>=a[i]+1,那么就是和制约2一样了,正所谓正难则反。而刚好,这里的n1的范围是8,1<<8不过256,可以接受容斥,然后问题就转化了nowm个球分成n组,直接隔板法c(m-1,n-1),也就是m-1个空隙砍n-1刀,套个ex_lucas板子就可以了,注意板子中可以预处理的地方,毕竟这题实现还是比较紧的。

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define f(i,a,b) for( int i=a;i<=b;++i)
#define ff(i,a,b) for( int i=a;i>=b;--i)
#define debug(x) cerr << #x << " : " << x << " " << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<string, string> pss;
const ll mod = 1e9 + 7;
const ll mod2 = 998244353;
const ll inf = 2e18;
const double tiaohe = 0.57721566490153286060651209;
ll oula(ll x) { ll res = x;f(i, 2, x / i) { if (x % i == 0) { res = res / i * (i - 1);while (x % i == 0) x /= i; } }if (x > 1) res = res / x * (x - 1);return res; }
ll quickmod(ll a, ll n, ll m) { ll s = 1;while (n) { if (n & 1) { s = s * a % m; }a = (a*a) % m;n = n / 2; }return s; }
ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; }
void ex_gcd(ll a, ll b, ll &x, ll &y, ll &d) { if (!b) { d = a, x = 1, y = 0; } else { ex_gcd(b, a % b, y, x, d);y -= x * (a / b); } }
ll inv(ll t, ll p) { ll d, x, y;ex_gcd(t, p, x, y, d);return d == 1 ? (x % p + p) % p : -1; }
bool isPrime(ll x) { if (x == 2)return true;if (x % 2 == 0)return false;for (ll i = 2;i*i <= x;i++) if (x % i == 0)return false; return true; }
inline ll in() { char ch = getchar();ll x = 0, f = 1;while (ch<'0' || ch>'9') { if (ch == '-')f = -1;ch = getchar(); }while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0';ch = getchar(); }return x * f; }
void print(double x) { printf("%.6lf\n", x); }
//double a = log(n) +tiaohe + 1.0 / (2 * n);
double eqa = (1 + sqrt(5.0)) / 2.0;
double E = 2.7182818284;
const double eps = 1e-8;
double pi = acos(-1);
const int N = 6e5 + 5;
ll t, p;
int n, n1, n2, m;
int a[N];
ll remain[60], mo[60], id, per[60];
ll china(ll n, ll a[], ll m[])
{
	ll M = 1, res = 0;
	f(i, 1, n)M *= m[i];
	f(i, 1, n) {
		ll w = M / m[i];
		res = (res + w * inv(w, m[i]) % M*a[i] % M) % M;
	}
	return (res + M) % M;
}
//另阶乘n!=19*18*......,p=3,c=2;
//你会发现这个阶乘可以转化成re*(块)*(块)*p^x*(x!)
//建议模拟下这个样例,然后就可以发现p^x可以拉出来算
//而每个块在%(p^c)的值是一样的(这里设为p^c),x!可以递归求,re可以暴力
ll f[200000];
ll fac(ll n, ll pi, ll pk)
{
	if (!n) return 1LL;
	if (n < pi) return f[n];
	return quickmod(f[pk - 1], n / pk, pk)*f[n%pk] % pk*fac(n / pi, pi, pk) % pk;
}
ll C(ll n, ll m, ll pi, ll pk)
{
	if (n < m) return 0;
	f[0] = 1;
	for (int i = 1;i <= pk;i++)
		if (i%pi != 0) f[i] = f[i - 1] * i%pk;
		else f[i] = f[i - 1];
	ll jn = fac(n, pi, pk), jm = fac(m, pi, pk), jnm = fac(n - m, pi, pk);
	int k = 0;
	for (ll i = n;i;i /= pi) k += i / pi;
	for (ll i = m;i;i /= pi) k -= i / pi;
	for (ll i = n - m;i;i /= pi) k -= i / pi;
	return jn * inv(jm, pk) % pk * inv(jnm, pk) % pk * quickmod(pi, k, pk) % pk;
}
//求解c(n,m)%p (p不一定为质数)
//考虑对p进行质因子分解p=p1^c1 * p2^c2........
//考虑c(n,m)%(pi^ci)==??
//这样可以构造出关于c(n,m)的一组同余方程,求解他就可以知道c(n,m)%p意义下的值
//现在问题的关键转移成求解c(n,m)%(pi^ci)
void get() {

	ll tmp = p;
	id = 0;
	for (ll i = 2;i*i <= tmp;i++)
	{
		if (tmp%i == 0)
		{
			int c = 0;
			ll now = 1;
			while (tmp%i == 0)c++, tmp /= i, now *= i;
			mo[++id] = now;
			per[id] = i;
		}
	}
	if (tmp > 1)
	{
		mo[++id] = tmp;
		per[id] = tmp;
	}
}
ll work(ll n, ll m)
{
	if (n < m)return 0;
	ll res = 0;
	f(i, 1, id) {
		remain[i] = C(n, m, per[i], mo[i]);
	}
	res = china(id, remain, mo);
	return res;
}
void ex_lucas() {
	//正难则反,x1<=a1不好求,转化成x1>=a1+1的问题,发现(1<<n1)可以容斥
	ll res = 0;
	f(i, 0, (1 << n1) - 1) {
		int now = 0;
		int c = 0;//不任意选的,即是x1>=a1+1这种,也是像第二种一样转化成隔板法
		f(j, 0, n1 - 1)
		{
			if (i >> j & 1) {
				now++;
				c += a[j + 1];
			}
		}
		if (now & 1) {
			res = (res - work(m - c - 1, n - 1) + p + p) % p;
		}
		else res = (res + work(m - c - 1, n - 1)) % p;
	}
	cout << res << endl;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif
	cin >> t >> p;
	get();
	while (t--) {
		cin >> n >> n1 >> n2 >> m;
		f(i, 1, n1 + n2)a[i] = in();
		f(i, n1 + 1, n2 + n1)m -= a[i] - 1;
		ex_lucas();
	}
	return 0;
}
上一篇:Saving Beans HDU - 3037(卢卡斯定理)


下一篇:lucas定理