题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=3129

题解:

容斥,扩展Lucas,中国剩余定理

先看看不管限制,只需要每个位置都是正整数时的方案数的求法。
假设有 N 个未知数,加起来的和为 M。
转化一下问题变为:"小球分配"
有 M 个相同的小球,放在 N 个盒子里,且每个盒子至少有一个的方案数。
那么方案数为 ${C}_{M-1}^{N-1}$
怎么理解这个式子呢?"插隔板法"。
使 M个小球放在一排,考虑可以在相邻小球的空隙间(共 M-1个空隙)插入一个隔板,总共插入 N-1个隔板。
把相邻的两个隔板(把首尾也看作另外2个隔板)中间的区域看做一个个的盒子,区域内的小球即为该盒子里的小球。
则任意一种插隔板的方法都对应一种把小球放入盒子的方法。
所以方案数为
${C}_{M-1}^{N-1}$

对于题目给的两种限制,不要被吓到了。
其实大于等于(>=W)这一个限制很好处理,直接把 M-=(W-1),即事先给这些位置分配 W-1个小球。
因为接下来按照上面的 "小球分配" 方式,这些限制一定可以满足。
然后对于小于等于(<=W) 就需要用到容斥了。
定义 f[S] 表示至少 S 集合里面的这些"小于等于"限制都不能满足,那么方案数的求法就是:
先强制给这些盒子分配 Wi 个小球,
使得接下来按照上面的 "小球分配" 方式求出方案数,这些盒子一定会超过限制。
所以按照容斥的做法:
答案 ANS = 至少 0 个盒子超出限制的方案数
                - 至少 1 个盒子超出限制的方案数
                +至少 2 个盒子超出限制的方案数
                -...+...

但是求那个方案数 C(N,M) 就比较麻烦了,因为 N,M很大,且模数不保证为质数,
所以用到 扩展Lucas(+国剩) 来求。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define _ % P
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
int h[300],k[300],d[300],pi[10],ppi[10],fc[10][100500],pnt;
int T,N,N1,N2,M,ANS;
void init(){ANS=0;memset(h,0,sizeof(h));memset(k,0,sizeof(k));memset(d,0,sizeof(d));
}
void pre_prime(int P){static bool np[100500];static int prime[100500],cnt;for(int i=2;i<=100000;i++){if(!np[i]){prime[++cnt]=i;if(P%i==0){pi[++pnt]=i; ppi[pnt]=1;while(P%i==0) ppi[pnt]*=i,P/=i;}}for(int j=1;j<=cnt&&i<=100000/prime[j];j++){np[i*prime[j]]=1;if(i%prime[j]==0) break;}}for(int i=1;i<=pnt;i++){fc[i][0]=1;for(int j=1;j<=100000;j++) fc[i][j]=(1ll*fc[i][j-1]*(j%pi[i]?j:1))%ppi[i];}
}
int pow(int a,int b,int P){int ret=1; a=(a)_;while(b){if(b&1) ret=(1ll*ret*a)_;a=(1ll*a*a)_; b>>=1;}return ret;
}
void exEuclidean(int a,int b,int &g,long long &x,long long &y){if(!b) g=a,x=1,y=0;else exEuclidean(b,a%b,g,y,x),y-=(a/b)*x;
}
int inv(int a,int P){int g; long long x,y;exEuclidean(a,P,g,x,y);x=((1ll*x)_+P)_;return (int)x;
}
int fac(int n,int I,int Pi,int P){if(!n) return 1; int ret=1,r=n%P;//for(int i=2;i<=P;i++) if(i%Pi) ret=(1ll*ret*i)_;ret=fc[I][P];ret=pow(ret,n/P,P);//for(int i=2;i<=r;i++) if(i%Pi) ret=(1ll*ret*i)_;ret=(1ll*ret*fc[I][r])_;return (1ll*ret*fac(n/Pi,I,Pi,P))_;
}
int C(int n,int m,int I,int Pi,int P){int x,y,z,c=0;x=fac(n,I,Pi,P); y=fac(m,I,Pi,P); z=fac(n-m,I,Pi,P);for(int i=n;i;i/=Pi) c+=i/Pi;for(int i=m;i;i/=Pi) c-=i/Pi;for(int i=n-m;i;i/=Pi) c-=i/Pi;return ((((1ll*x*inv(y,P))_)*inv(z,P))_*pow(Pi,c,P))_;
}
int exLucas(int n,int m,int P){int ret=0,tmp; if(n<m) return 0;for(int i=1;i<=pnt;i++){tmp=C(n,m,i,pi[i],ppi[i]);tmp=((1ll*tmp*(P/ppi[i]))_*inv(P/ppi[i],ppi[i]))_;ret=(1ll*ret+tmp)_;}return ret;
}
int main()
{int P;scanf("%d%d",&T,&P);pre_prime(P);while(T--){init();scanf("%d%d%d%d",&N,&N1,&N2,&M);for(int i=1;i<=N1;i++) scanf("%d",&d[1<<(i-1)]);for(int i=1,x;i<=N2;i++) scanf("%d",&x),M-=(x-1);for(int s=1,p;p=s&-s,s<(1<<N1);s++)h[s]=h[s^p]+1,k[s]=k[s^p]+d[p];for(int s=0,m,tmp;s<(1<<N1);s++){m=M-k[s];tmp=exLucas(m-1,N-1,P);        if(h[s]&1) tmp=(-1ll*tmp+P)_;ANS=((1ll*ANS+tmp)_+P)_;}printf("%d\n",ANS);}return 0;
}

转载于:https://www.cnblogs.com/zj75211/p/8040188.html

●BZOJ 3129 [Sdoi2013]方程相关推荐

  1. P3301 [SDOI2013]方程

    P3301 [SDOI2013]方程 题意: 题解: 插板法介绍 首先要先讲组合数学的一个方法:插板法 问题引出:把10个球放进三个盒子,每个箱子至少一个有多少种分法? 10个球就有9个空隙,我们可以 ...

  2. 洛谷 2312 / bzoj 3751 解方程——取模

    题目:https://www.luogu.org/problemnew/show/P2312 https://www.lydsy.com/JudgeOnline/problem.php?id=3751 ...

  3. bzoj 3131 [Sdoi2013]淘金(数位dp)

    题目描述 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块. 一阵风吹过,金子的位置发生了 ...

  4. bzoj3129 [Sdoi2013]方程 容斥+扩展lucas

    调了两天,全是低级错误和脑子问题 对于>=的,直接用m减掉 对于<=的,一开始想枚举减多少,但发现对于每个枚举的值是独立的,所以是1e9的 然后由于>=和<=是两个对立的情况, ...

  5. BZOJ 3203 Sdoi2013 保护出题人 凸包+三分

    题目大意:太长自己看 令sumi表示第i个僵尸以及之前的僵尸的体力总和.disi表示第i个僵尸与房屋的初始距离 我们发现我们能消灭一个僵尸当且仅当y>=sumidisi 那么我们要求的显然就是m ...

  6. BZOJ 3131 [Sdoi2013]淘金

    题解: 首先要看出行列独立 令f[i]表示挂到i点的数量 则(i,j)的金币数量为f[i]*f[j] 然后数位DP求出f[i] 转载于:https://www.cnblogs.com/zzyer/p/ ...

  7. bzoj 乱刷计划 50/50

    前言 话说第一个板刷计划由于种种原因而告一段落了..其实那一版还有很多题想做,那就只能放一放了 附上效果图一张(几乎每一题都在我博客有题解): 打算 可以复习,重做自己做过的题,不局限于没做过的 乱刷 ...

  8. 一句话题解(20180210~)

    2.9 BZOJ 2006 [NOI2010]超级钢琴.这道题目几天之前就做了.做法是固定右端点,左端点在ST表上走,走法其实就是笛卡尔树的走法.完结撒花! BZOJ 1218 [HNOI2003]激 ...

  9. 组合数学学习笔记(未完待续

    这学期学了不少组合数学,期末给他补完. 算法竞赛考得很多的部分啊 这个还是很重要的 在目前的算法竞赛中有三大计数考点 1)组合计数 2)线性计数 3)群论计数 其中群论计数比较困难,我又不知道什么是线 ...

最新文章

  1. bat 调用python
  2. ContentProvider初探
  3. shiro教程(3)-shiro授权
  4. 为什么vs数据库中文显示问号_第005期 | 写论文为什么不应写别人没写过的题目?...
  5. 腾讯云Centos升级python2到python3
  6. oracle获取当前日期的前一天,判断两个日期是否相等
  7. linux内核设计与实现 怎么读,《Linux内核设计与实现》读书笔记(一)
  8. chkconfig: 2345 20 80
  9. LSTM模型在问答系统中的应用
  10. 将军令:数据安全平台建设实践 1
  11. HBase 表修复在线方式和离线方式
  12. 成考计算机专业难不难,成人高考计算机专业难吗?
  13. Canonical标签怎么使用,Canonical标签有什么作用?
  14. 让人惊愕的coffee spitter
  15. 私密聊天加密聊天伪装界面聊天软件产品UI分析,qiaoyu5,feige5
  16. 【杰神说说】物联大师2.0版本预告
  17. Argis ArcToolbox-分割栅格,无结果**
  18. python甜橙歌曲音乐网站平台源码
  19. 电商销售数据分析与预测(日期数据统计、按天统计、按月统计)
  20. js回调函数的用途与写法

热门文章

  1. php 去掉数组中的空值_PHP删除数组中空值的方法介绍
  2. 禅道 bug状态 open_小工具大帮手,利用 @open-node/antman 实现 node.js 进程线上调试,无须重启...
  3. 五天学习Mysql数据库教程(一)1.1数据库的基本概念
  4. python如何输入多组数据_Python 中如何实现多组的输入输出
  5. 并查集路径压缩_并查集(UnionFind)技巧总结
  6. python导入自定义文件_python引入导入自定义模块和外部文件的实例
  7. udp 传输文件 java_Java 网络编程 之 UDP 文件传输
  8. select框怎么传值到服务端_前端简历中的项目经历怎么突出亮点?
  9. 这一大波电子“艺术”图,美翻了!
  10. 攻城狮危险:波士顿动力机器狗去福特当工程师了!