ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

扩展lucas+容斥

2021-03-22 18:58:38  阅读:198  来源: 互联网

标签:ch return lucas int 容斥 ll 扩展 res pk


扩展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;
}

标签:ch,return,lucas,int,容斥,ll,扩展,res,pk
来源: https://blog.csdn.net/qq_43543086/article/details/115091467

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有