[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
", (End - Beg) / CLOCKS_PER_SEC);
  return 0;
}
原文地址:https://www.cnblogs.com/mrclr/p/10959670.html