BZOJ 3129 SDOI2013 方程

如果没有限制,答案直接用隔板法C(m-1,n-1)

对于>=x的限制,我们直接在对应位置先放上x-1即可,即m=m-(x-1)

对于<=x的限制,由于限制很小我们可以利用容斥原理将它转化为上面的>=x的限制

即减去1个不满足的 加上2个不满足的 减去3个不满足的 ……

之后就是组合数的计算,对于一个非常大的模数,我们可以将它唯一分解,之后CRT还原即可

但是我们有可能不存在逆元,数据范围不允许我们递推计算组合数

我们知道没有逆元当且仅当(a,p)不互素,我们可以将阶乘分成两部分:互素和不互素

互素的部分具有循环节,我们暴力计算循环节之后快速幂即可

不互素的部分我们可以提出他们的gcd出来并记录指数,剩余部分又变成了计算阶乘,递归即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;

const int maxn=110;
typedef long long LL;
int T,tot=0;
LL p,n,n1,n2,m;
LL mod[maxn],prime[maxn],a[maxn];
LL Num[maxn];
LL x,y,d;
LL ans;

void Get_mod(LL x){
    for(int i=2;i*i<=x;++i){
        if(x%i==0){
            prime[++tot]=i;mod[tot]=1;
            while(x%i==0){x/=i;mod[tot]*=i;}
        }
    }
    if(x>1)prime[++tot]=x,mod[tot]=x;
}
void ex_gcd(LL a,LL b,LL &x,LL &y,LL &d){
    if(b==0){x=1;y=0;d=a;}
    else{ex_gcd(b,a%b,y,x,d);y-=(a/b)*x;}
}
LL pow_mod(LL v,LL p,LL mod){
    LL tmp=1;
    while(p){
        if(p&1)tmp=tmp*v%mod;
        v=v*v%mod;p>>=1;
    }return tmp;
}
LL inv(LL a,LL b){
    ex_gcd(a,b,x,y,d);
    return (x%b+b)%b;
}
LL CRT(){
    LL ans=0;
    for(int i=1;i<=tot;++i){
        LL m=p/mod[i];
        ex_gcd(mod[i],m,d,y,d);
        ans=(ans+y*m*a[i])%p;
    }return (ans+p)%p;
}
pair<LL,LL> fac(int k,LL n){
    if(n==0)return make_pair(0,1);
    int x=n/prime[k],y=n/mod[k];
    LL ans=1;
    if(y){
        for(int i=2;i<mod[k];++i)if(i%prime[k]!=0)ans=(ans*1LL*i)%mod[k];
        ans=pow_mod(ans,y,mod[k]);
    }
    for(int i=y*mod[k]+1;i<=n;++i)if(i%prime[k]!=0)ans=(ans*1LL*i)%mod[k];
    pair<LL,LL> tmp=fac(k,x);
    return make_pair(x+tmp.first,ans*tmp.second%mod[k]);
}
LL cal(int k,LL n,LL m){
    if(n<m)return 0;
    pair<LL,LL> a=fac(k,n),b=fac(k,m),c=fac(k,n-m);
    return pow_mod(prime[k],a.first-b.first-c.first,mod[k])*a.second%mod[k]
    *inv(b.second,mod[k])%mod[k]*inv(c.second,mod[k])%mod[k];
}
LL C(LL n,LL m){
    for(int i=1;i<=tot;++i)a[i]=cal(i,n,m);
    return CRT();
}
void DFS(int pos,int now,LL sum){
    if(pos>n1){
        if(now)ans-=C(m-1-sum,n-1);
        else ans+=C(m-1-sum,n-1);
        ans=(ans+p)%p;
        return;
    }
    DFS(pos+1,now,sum);
    DFS(pos+1,now^1,sum+Num[pos]);
}

int main(){
    scanf("%d%lld",&T,&p);
    Get_mod(p);
    while(T--){
        scanf("%lld%lld%lld%lld",&n,&n1,&n2,&m);
        for(int i=1;i<=n1;++i)scanf("%lld",&Num[i]);
        for(int i=1;i<=n2;++i){scanf("%lld",&x);m=m-x+1;}
        ans=0;
        DFS(1,0,0);
        printf("%lld\n",(ans+p)%p);
    }return 0;
}

  

时间: 2024-10-13 23:28:44

BZOJ 3129 SDOI2013 方程的相关文章

[BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】

题目链接:BZOJ - 3129 题目分析 使用隔板法的思想,如果没有任何限制条件,那么方案数就是 C(m - 1, n - 1). 如果有一个限制条件是 xi >= Ai ,那么我们就可以将 m 减去 Ai - 1 ,相当于将这一部分固定分给 xi,就转化为无限制的情况了. 如果有一些限制条件是 xi <= Ai 呢?直接来求就不行了,但是注意到这样的限制不超过 8 个,我们可以使用容斥原理来求. 考虑容斥:考虑哪些限制条件被违反了,也就是说,有哪些限制为 xi <= Ai 却是 xi

BZOJ 3129 [Sdoi2013]方程 不定方程解的个数+组合数取模

题意:链接 方法:不定方程解的个数+组合数取模 解析: 先看n1与n2的部分的限制. 对于后半部分的限制来说,我们直接减去An1+i?1就可以转化一下求正整数解. 但是前半部分呢? 跟上一道猴子那个很像. 所以我们容斥搞就行了. 但是这道题好像不好写的地方不在这? 这题TMD不就是礼物吗! 大组合数取模如何取? 请参见我<BZOJ 礼物>的题解. 另外吐槽题干 明明是X1+X2+-+Xn=m 并不是小于等于 代码: #include <cstdio> #include <cs

●BZOJ 3129 [Sdoi2013]方程

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3129 题解: 容斥,扩展Lucas,中国剩余定理 先看看不管限制,只需要每个位置都是正整数时的方案数的求法.假设有 N 个未知数,加起来的和为 M.转化一下问题变为:"小球分配" 有 M 个相同的小球,放在 N 个盒子里,且每个盒子至少有一个的方案数. 那么方案数为 ${C}_{M-1}^{N-1}$怎么理解这个式子呢?"插隔板法".使 M个小球放在一排,考虑

bzoj 3198: [Sdoi2013]spring 题解

[原题] 3198: [Sdoi2013]spring Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 253  Solved: 95 Description Input Output Sample Input 3 3 1 2 3 4 5 6 1 2 3 0 0 0 0 0 0 4 5 6 Sample Output 2 HINT [题解]这道题明明是水题,坑了我两天!!!真是伤心.发现哈希都不熟练了. 首先很容易想到是2^6枚举01状态,使得1

bzoj 3129

非常好的一道数学题,考察了大量数论和组合数学的知识 在做本题之前强烈建议先完成下列两个背景知识: ①: bzoj 2142礼物 因为本题的一部分数据需要利用到拓展卢卡斯定理,而礼物是拓展卢卡斯定理的裸题,先做礼物是一个比较好的选择 有困难戳这里https://blog.csdn.net/lleozhang/article/details/82884768 ②: CF451E 本题的核心思想和CF451E完全相同,CF451E稍简单一些,所以先理解这里的思想再做本题会发现难度降了不少 有困难戳这里

[BZOJ 3198] [Sdoi2013] spring 【容斥 + Hash】

题目链接:BZOJ - 3198 题目分析 题目要求求出有多少对泉有恰好 k 个值相等. 我们用容斥来做. 枚举 2^6 种状态,某一位是 1 表示这一位相同,那么假设 1 的个数为 x . 答案就是 sigma((-1)^(x - k) * AnsNow * C(x, k)) .注意 x 要大于等于 k. 对于一种状态,比如 10110,就是要保证第 1, 3, 4 个值相同. 这些值相同的对数怎么来求呢?使用Hash. 将这些位上的值 Hash 成一个数,然后枚举  [1, i] , 每次求

BZOJ 3130: [Sdoi2013]费用流 网络流+二分

3130: [Sdoi2013]费用流 Time Limit: 10 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1230  Solved: 598[Submit][Status][Discuss] Description Alice和Bob在图论课程上学习了最大流和最小费用最大流的相关知识.     最大流问题:给定一张有向图表示运输网络,一个源点S和一个汇点T,每条边都有最大流量.一个合法的网络流方案必须满足:(1)每条边的实际流量都

Bzoj 3124: [Sdoi2013]直径 题解

3124: [Sdoi2013]直径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1222  Solved: 580[Submit][Status][Discuss] Description 小Q最近学习了一些图论知识.根据课本,有如下定义.树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度.如果一棵树有N个节点,可以证明其有且仅有N-1 条边. 路径:一棵树上,任意两个节点之间最多有一条简单路径.我们用 dis(a,b)表示点a和点

Bzoj 3131 [Sdoi2013]淘金 题解

3131: [Sdoi2013]淘金 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 733  Solved: 363[Submit][Status][Discuss] Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块.    一阵风吹过,金子的位置发生了一些变化.细心的小Z发现,初始在(i,j)坐标处的金子会变到(f(i),