LOJ3075 「2019 集训队互测 Day 3」组合数求和
题意:
令 \(f(j)=\sum\limits_{i=0}^{n-1}\dbinom{id}{j}\) ,对于 \(0\le j <m\) ,分别求出 \(f(j)\) 。答案对 \(M\) 取模。
\(10^8\le M\le 10^9\) (不保证是素数) 。
\(1\le nd\le 10^9,1\le md\le 3*10^6,1\le d\le 100\) 。
做法:
step1
首先答案能用生成函数的角度求出:令 \(C(x)=\sum\limits_{i=0}^{n-1} (1+x)^{id}\),则 \(f(j)=[x^j]C(x)\) 。 就是求 \(C(x)\) 的前 \(m\) 项系数。
考虑这是等比数列求和,令 \(A(x)=\frac{(1+x)^{nd}-1}{x},B(x)=\frac{(1+x)^{d}-1}{x}\) ,则 \(C(x)=\frac{A(x)}{B(x)}\) 。
step2
\(A\) 和 \(B\) 都是容易求出的,把模数拆成 \(M=\prod p_i^{c_i}\) 的形式,求出模 \(p_i^{c_i}\) 时的一行组合数是不难的(记录一下 \(p_i\) 的次数,其他的正常乘),然后再 CRT 合并。
这一部分复杂度大概是 \(O((m+d)\log n)\) 的。
step3
令 \(f_i=[x^i]A(x),g_i=[x^i]B(x),h_i=[x^i]C(x)\) 。
先考虑 \(gcd(d,M)=1\) 这档分, \(g_0h_i=f_i-\sum\limits_{j=1}^{d-1}g_jh_{i-j}\) 。
因为 \(g_0=d\) ,所以可以求逆元,我们就能 \(O(md)\) 算出来。
\(gcd(d,M)\ne 1\) 怎么办?到这里我就不会做了/kk
step4
还是把 \(M\) 拆成 \(\prod p_i^{c_i}\) 的形式,把模 \(p^C\) 的答案算出来,再 CRT 合并。注意 \(d\) 不是 \(p_i\) 倍数的部分,可以乘在一起,用上面的方法处理。
对于 \(d\) 是 \(p\) 的倍数,找出最小的 \(t\) 使 \(g_t\) 不是 \(p\) 的倍数。由于 \(g_{d-1}=1\) ,我们是一定能找出来的。
则 \(h_ig_t=f_{i+t}-\sum\limits_{j\ne t}g_jh_{i+t-j}\) 。(称为式子 \(P\))
\(g_t\) 是能求逆元的,但是我们的式子中存在 “高” 贡献给 "低" 的情况,这是不好的。
我们以 \(i\) 为主元,维护 \(a\) 和 \(c\),表示 \(h_i=\sum a_j f_{i+j}+\sum c_j h_{i+j}\) 。
我们把式子 \(P\) 用上面的方式表达出来,记为式子 \(Q\) 。
接下来展示一种方法,使得能把一个初始 \(=Q\) 的式子 \(R\) ,通过变换,使得 \(R\) 中对于 \(j>0\) 都有 \(c_j\) 为 \(p^C\) 倍数。利用 \(R\) ,就能以 \(O(m|R|)\) 的时间递推出 \(h\) 。
(只存在 “低” 贡献给 “高” 了)
根据 \(t\) 的最小化的定义,变换前 \(R\) 满足:对于 \(j>0\) 都有 \(c_j\) 为 \(p^1\) 的倍数。
如果对于第 \(i\) 轮操作,初始对于 \(j>0\) 都有 \(c_j\) 为 \(p^i\) 倍数 ,我们能通过变换使得 \(c_j\) 都为 \(p^{i+1}\) 的倍数。则通过 \(C-1\) 轮操作使 \(R\) 合法。
而操作的方法就是,从大到小枚举 \(j>0\) ,如果当前 \(c_j\) 不是 \(p^{i+1}\) 的倍数,就把 \(h_{i+j}\) 利用 \(Q\) 展开。(展开 \(h_{i+j}\) ,也就是把 \(Q\) 中的 \(i\) 用 \(i+j\) 替换)
此时,\(k\geq j\) 都满足是 \(p^{i+1}\) 的倍数,且 \(k<j\) 仍满足是 \(p^i\) 的倍数。
则每一轮操作的时间复杂度是 \(O(d|R|)\) 。因为每次 \(|R|\) 会增加至多 \(d-1\) ,所以求 \(R\) 总复杂度是 \(O(C^2d^2)\) ,最后 \(|R|\) 能达到 \(O(Cd)\) 的长度。
得出模 \(p^C\) 的复杂度是 \(O(Cmd+C^2d^2)\) 。
最后得到的 \(R\) 有可能 \(c_0\ne 0\) ,但它一定是 \(p\) 的倍数,所以 \(gcd(1-c_0,p^C)=1\) ,再求一下逆元就好了。
总结
总复杂度为 \(O(md\log M+d^2\log^2 M+(m+d)\log n)\) 。
自己没想到的东西:
拆模数,求模数为 \(p_i^{c_i}\) 的答案后 CRT 合并。这是很常见的方法。
处理不好求逆元的多项式除法的神秘方法。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,d,M,mod;
int P[10],C[10],t;
void exgcd(int a,int b,ll &x,ll &y){
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x),y-=(a/b)*x;
}
int inv(int a,int b){ll x,y;exgcd(a,b,x,y);return (x%b+b)%b;}
int A[3010000],B[110];
void so(int p,int c,int xs){
int mo=1;
for(int i=1;i<=c;i++)mo*=p;
int Z=1,PX=0;
for(int i=1;i<=m+10010;i++){
int a=n-i+1;
if(!a)break;
while(!(a%p))a/=p,PX++;
Z=1ll*Z*a%mo;
a=i;while(!(a%p))a/=p,PX--;
Z=1ll*Z*inv(a,mo)%mo;
int az=Z,pt=PX;
if(pt>=c)az=0;
else{while(pt--)az=1ll*p*az%mod;}
(A[i-1]+=1ll*az*xs%mod)%=mod;
}
Z=1,PX=0;
for(int i=1;i<=d;i++){
int a=d-i+1;
while(!(a%p))a/=p,PX++;
Z=1ll*Z*a%mo;
a=i;while(!(a%p))a/=p,PX--;
Z=1ll*Z*inv(a,mo)%mo;
int az=Z,pt=PX;
if(pt>=c)az=0;
else{while(pt--)az=1ll*p*az%mod;}
(B[i-1]+=1ll*az*xs%mod)%=mod,(B[i-1]+=mod)%=mod;
}
}
int h[3010000];
int xa[10100],xc[10100];
int u[3001000];
void ot(int p,int c,int xs){
int mo=1;
for(int i=1;i<=c;i++)mo*=p;
memset(xa,0,sizeof(xa));memset(xc,0,sizeof(xc));
int t=d-1;
for(int i=0;i<d;i++)if(B[i]%p){t=i;break;}
int I=inv(B[t],mo),k=d-1-t;
xa[d-1]=I;
for(int i=0;i<d;i++)if(i!=k)xc[i]=(mo-1ll*B[d-1-i]*I%mo)%mo;
int L=d-1;
while(L>k&&!xc[L])L--;
while(L>k){
for(int i=L;i>k;i--)if(xc[i]){
(xa[i+d-1-k]+=1ll*xc[i]*I%mod)%=mo;
for(int j=0;j<d;j++)if(j!=k)(xc[i+(j-k)]+=1ll*(mo-1ll*B[d-1-j]*I%mo)%mo*xc[i]%mo)%=mo;
xc[i]=0;
}
L+=d;
while(L&&!xc[L])L--;
}
L=(d-1)*c;
while(L>=k&&!xa[L])L--;
I=inv(((1-xc[k])%mo+mo)%mo,mo);
for(int i=0;i<k;i++)xc[i]=1ll*xc[i]*I%mod;
for(int i=k;i<=L;i++)xa[i]=1ll*xa[i]*I%mod;
for(int i=0;i<m;i++){
int z=0;
for(int j=k;j<=L;j++)(z+=1ll*A[i+(j-k)]*xa[j]%mo)%=mo;
for(int j=0;j<k;j++)if(i>=k-j)(z+=1ll*xc[j]*u[i-(k-j)]%mo)%=mo;
u[i]=z,(h[i]+=1ll*u[i]*xs%mod)%=mod;
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&d,&M);n*=d;
mod=M;
for(int i=2;i*i<=M;i++)if(!(M%i)){
t++,P[t]=i,C[t]=0;
while(!(M%i))M/=i,C[t]++;
}
if(M>1)t++,P[t]=M,C[t]=1;
for(int i=1;i<=t;i++){
int z=1;
for(int j=1;j<=C[i];j++)z*=P[i];
so(P[i],C[i],1ll*(mod/z)*inv(mod/z,z)%mod);
}
int pc=1;
for(int i=1;i<=t;i++)if(d%P[i])while(C[i]--)pc*=P[i];
int I=inv(B[0],pc),xs=1ll*(mod/pc)*inv(mod/pc,pc)%mod;
for(int i=0;i<m;i++){
h[i]=A[i];
for(int j=1;j<d;j++)if(j<=i)(h[i]-=1ll*B[j]*h[i-j]%pc)%=pc;
h[i]=1ll*h[i]*I%pc;
}
for(int i=0;i<m;i++)h[i]=(1ll*xs*h[i]%mod+mod)%mod;
for(int i=1;i<=t;i++)if(!(d%P[i])){
int z=1;for(int j=1;j<=C[i];j++)z*=P[i];
ot(P[i],C[i],1ll*(mod/z)*inv(mod/z,z)%mod);
}
int anss=0;
for(int i=0;i<m;i++)anss^=((h[i]+mod)%mod);
return printf("%d",anss),0;
}
LOJ3075 「2019 集训队互测 Day 3」组合数求和的更多相关文章
- LOJ3069. 「2019 集训队互测 Day 1」整点计数(min_25筛)
题目链接 https://loj.ac/problem/3069 题解 复数真神奇. 一句话题意:令 \(f(x)\) 表示以原点 \((0, 0)\) 为圆心,半径为 \(x\) 的圆上的整点数量, ...
- LOJ2476. 「2018 集训队互测 Day 3」蒜头的奖杯 & LOJ2565. 「SDOI2018」旧试题(莫比乌斯反演)
题目链接 LOJ2476:https://loj.ac/problem/2476 LOJ2565:https://loj.ac/problem/2565 题解 参考照搬了 wxh 的博客. 为了方便, ...
- 【LOJ2461】「2018 集训队互测 Day 1」完美的队列(分块+双指针)
点此看题面 大致题意: 让你维护\(n\)个有限定长度的队列,每次区间往队列里加数,求每次加完后的队列里剩余元素种类数. 核心思路 这道题可以用分块+双指针去搞. 考虑求出每个操作插入的元素在队列中被 ...
- @loj - 2461@ 「2018 集训队互测 Day 1」完美的队列
目录 @description@ @solution@ @part - 0@ @part - 1@ @accepted code@ @details@ @description@ 小 D 有 n 个 ...
- 【loj2461】【2018集训队互测Day 1】完美的队列
#2461. 「2018 集训队互测 Day 1」完美的队列 传送门: https://loj.ac/problem/2461 题解: 直接做可能一次操作加入队列同时会弹出很多数字,无法维护:一个操作 ...
- 【2018集训队互测】【XSY3372】取石子
题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...
- 洛谷 P4463 - [集训队互测 2012] calc(多项式)
题面传送门 & 加强版题面传送门 竟然能独立做出 jxd 互测的题(及其加强版),震撼震撼(((故写题解以祭之 首先由于 \(a_1,a_2,\cdots,a_n\) 互不相同,故可以考虑求出 ...
- 【纪中集训2019.3.27】【集训队互测2018】小A的旅行(白)
题目 描述 \(0-n-1\)的图,满足\(n\)是\(2\)的整数次幂, $ i \to j $ 有 $ A_{i,j} $ 条路径: 一条路径的愉悦值定义为起点和终点编号的\(and\)值 ...
- UOJ#191. 【集训队互测2016】Unknown 点分治 分治 整体二分 凸包 计算几何
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ191.html 题目传送门 - UOJ191 题意 自行移步集训队论文2016中罗哲正的论文. 题解 自行 ...
- UOJ#191. 【集训队互测2016】Unknown
题意:维护一个数列,每个元素是个二维向量,每次可以在后面加一个元素或者删除一个元素.给定P(x,y),询问对于[l,r]区间内的元素$S_i$,$S_i \times P$的最大值是多少. 首先简单地 ...
随机推荐
- 【JVM】经典垃圾回收器
本文已收录至Github,推荐阅读 Java随想录 微信公众号:Java随想录 CSDN: 码农BookSea 转载请在文首注明出处,如发现恶意抄袭/搬运,会动用法律武器维护自己的权益.让我们一起维护 ...
- Go语言使用场景 | go语言与其它开源语言比较 | Go WEB框架选型
一.Go语言使用场景 1. 关于go语言 2007年,受够了C++煎熬的Google首席软件工程师Rob Pike纠集Robert Griesemer和Ken Thompson两位牛人,决定创造一种新 ...
- 解读JVM级别本地缓存Caffeine青出于蓝的要诀3 —— 讲透Caffeine的数据驱逐淘汰机制与用法
大家好,又见面了. 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面.如果感兴趣,欢迎关注以获取后续更新. 上一篇文章中,我们聊了下Caffein ...
- javascript计算器
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- [OpenCV实战]35 使用Tesseract和OpenCV实现文本识别
目录 1 如何在Ubuntu和windows上安装Tesseract 1.1 在ubuntu18.04上安装Tesseract4 1.2 在Ubuntu 14.04,16.04,17.04,17.10 ...
- jQuery查找标签、操作标签、事件和动画效果,Bootstrap页面框架的介绍和使用讲解
今日内容 jQuery查找标签 1.基本选择器: $('#d1') id选择器 $('.c1') class选择器 $('div') 标签选择器 2.组合选择器: $('div#d1') 查找id是d ...
- 高性能 Java 框架。Solon v1.12.3 发布(春节前兮的最后更)
一个更现代感的 Java "生态型"应用开发框架:更快.更小.更自由.不是 Spring,没有 Servlet,也无关 JavaEE:新兴独立的轻量生态 (已有150来个生态插件) ...
- py教学之列表
列表是什么 list 是一些元素按照一定顺序排列的元素集合 序列是 Python 中最基本的数据结构. 序列中的每个值都有对应的位置值,称之为索引,第一个索引是 0,第二个索引是 1,依此类推. Py ...
- MySQL 留存率和复购率的场景分析
实际工作中常见的业务场景是求次日留存率,还有一些会对次日留存率增加限制,例如求新用户的次日留存率或者求活跃用户留存率.另外,留存率和复购率看起来都是统计重复出现的概率,但实际求解方法是不一样的. [场 ...
- Xmake v2.7.6 发布,新增 Verilog 和 C++ Modules 分发支持
Xmake 是一个基于 Lua 的轻量级跨平台构建工具. 它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时. 它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLis ...