P3301 [SDOI2013]方程

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3301 [SDOI2013]方程相关的知识,希望对你有一定的参考价值。

P3301 [SDOI2013]方程

题意:

题解:

插板法介绍

首先要先讲组合数学的一个方法:插板法

问题引出:把10个球放进三个盒子,每个箱子至少一个有多少种分法?

10个球就有9个空隙,我们可以考虑在这个9个空隙中放入两个隔板,这样10个球就被分成了3组,就相当于放入了三个箱子。
答案就是 C 10 − 1 3 − 1 C_{10-1}^{3-1} C10131
也就是n个球放入m个盒子,每个箱子至少一个有 C n − 1 m − 1 C_{n-1}^{m-1} Cn1m1种分法

问题2:把10个球放进三个盒子有多少种分法

此时箱子内可以没有球,我们可以预先在3个盒子里都放一个球,则问题转化为将13个球放进3个盒子里,每个盒子至少一个有多少中放法。答案为 C 12 2 C_{12}^2 C122
也就是n个球放入m个盒子,有 C n + m − 1 m − 1 C_{n+m-1}^{m-1} Cn+m1m1种分法

本题讲解:

如果没有限制,就是求 x 1 + x 2 + . . + x n = M x_{1}+x_{2}+..+x_{n}=M x1+x2+..+xn=M,这不就相当于把M分配到n个箱子里,且每个箱子不能为空,这样答案就是 C M − 1 n − 1 C_{M-1}^{n-1} CM1n1
但是题目有两类限制:

第二类限制为: X i > = A i X_{i}>=A_{i} Xi>=Ai,我们可以巧妙的转化,对于第i个箱子要求分配数要大于 A i A_{i} Ai,那我们可以认为先将 A i − 1 A_{i}-1 Ai1分配给 X i X_{i} Xi,然后剩下还是老分法(分配给n个箱子,每个箱子至少一个)。这种方法M要减去 A i − 1 A_{i}-1 Ai1(相当于提前分配了)

对于第一类限制, X i < = A i X_{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 ∗ 1007 262203414=2*3*11*397*1007 262203414=23113971007
437367875 = 5 3 ∗ 7 3 ∗ 10 1 2 437367875=5^3*7^3*101^2 437367875=53731012
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 LOCAL
    startTime= clock();
    freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef LOCAL
    endTime= 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=1bzoj3129[Sdoi2013]方程 exlucas+容斥原理

[SDOI2013]方程

●BZOJ 3129 [Sdoi2013]方程

BZOJ3129 [Sdoi2013]方程 扩展Lucas

[SDOI2006] 二进制方程

P2455 [SDOI2006]线性方程组