题意:

令 \(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)\) 。

自己没想到的东西:

  1. 拆模数,求模数为 \(p_i^{c_i}\) 的答案后 CRT 合并。这是很常见的方法。

  2. 处理不好求逆元的多项式除法的神秘方法。

代码:

#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」组合数求和的更多相关文章

  1. LOJ3069. 「2019 集训队互测 Day 1」整点计数(min_25筛)

    题目链接 https://loj.ac/problem/3069 题解 复数真神奇. 一句话题意:令 \(f(x)\) 表示以原点 \((0, 0)\) 为圆心,半径为 \(x\) 的圆上的整点数量, ...

  2. LOJ2476. 「2018 集训队互测 Day 3」蒜头的奖杯 & LOJ2565. 「SDOI2018」旧试题(莫比乌斯反演)

    题目链接 LOJ2476:https://loj.ac/problem/2476 LOJ2565:https://loj.ac/problem/2565 题解 参考照搬了 wxh 的博客. 为了方便, ...

  3. 【LOJ2461】「2018 集训队互测 Day 1」完美的队列(分块+双指针)

    点此看题面 大致题意: 让你维护\(n\)个有限定长度的队列,每次区间往队列里加数,求每次加完后的队列里剩余元素种类数. 核心思路 这道题可以用分块+双指针去搞. 考虑求出每个操作插入的元素在队列中被 ...

  4. @loj - 2461@ 「2018 集训队互测 Day 1」完美的队列

    目录 @description@ @solution@ @part - 0@ @part - 1@ @accepted code@ @details@ @description@ 小 D 有 n 个 ...

  5. 【loj2461】【2018集训队互测Day 1】完美的队列

    #2461. 「2018 集训队互测 Day 1」完美的队列 传送门: https://loj.ac/problem/2461 题解: 直接做可能一次操作加入队列同时会弹出很多数字,无法维护:一个操作 ...

  6. 【2018集训队互测】【XSY3372】取石子

    题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...

  7. 洛谷 P4463 - [集训队互测 2012] calc(多项式)

    题面传送门 & 加强版题面传送门 竟然能独立做出 jxd 互测的题(及其加强版),震撼震撼(((故写题解以祭之 首先由于 \(a_1,a_2,\cdots,a_n\) 互不相同,故可以考虑求出 ...

  8. 【纪中集训2019.3.27】【集训队互测2018】小A的旅行(白)

    题目 描述 ​ \(0-n-1\)的图,满足\(n\)是\(2\)的整数次幂, $ i \to j $ 有 $ A_{i,j} $ 条路径: ​ 一条路径的愉悦值定义为起点和终点编号的\(and\)值 ...

  9. UOJ#191. 【集训队互测2016】Unknown 点分治 分治 整体二分 凸包 计算几何

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ191.html 题目传送门 - UOJ191 题意 自行移步集训队论文2016中罗哲正的论文. 题解 自行 ...

  10. UOJ#191. 【集训队互测2016】Unknown

    题意:维护一个数列,每个元素是个二维向量,每次可以在后面加一个元素或者删除一个元素.给定P(x,y),询问对于[l,r]区间内的元素$S_i$,$S_i \times P$的最大值是多少. 首先简单地 ...

随机推荐

  1. EXACT函数

    EXACT函数:EXACT函数是一个文本函数,通过这个函数可以将不同的字符串进行对比,通常用于信息核对. EXACT函数的功能:比较两个字符串是否一致,返回不同的结果. EXACT函数的语法结构:EX ...

  2. Graph Neural Network——图神经网络

    本文是跟着李沐老师的论文精度系列进行GNN的学习的,详细链接请见:零基础多图详解图神经网络(GNN/GCN)[论文精读] 该论文的标题为<A Gentle Introduction to Gra ...

  3. java中的递归机制

    本文主要讲述java中的递归机制. 示例1,递归代码如下: public class Recursion01 { public static void main(String[] args) { T ...

  4. 一个开放源代码,实现动态IL注入(Hook或补丁工具)框架:Lib.Harmony(Patch,PatchAll,Prefix,Postfix,Transpiler)

    详情请参考原文:一个开放源代码,实现动态IL注入(Hook或补丁工具)框架:Lib.Harmony  

  5. [python]《Python编程快速上手:让繁琐工作自动化》学习笔记6

    1. 发送电子邮件和短信笔记(第16章)(代码下载) 1.1 发送电子邮件 简单邮件传输协议(SMTP)是用于发送电子邮件的协议.SMTP 规定电子邮件应该如何格式化.加密.在邮件服务器之间传递,以及 ...

  6. P1278 单词游戏【题解】(状压dp)

    单词游戏 题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单词禁止说两遍 ...

  7. MySQL 索引的创建、删除

    MySQL中索引的创建有三种方法,索引的删除有两种方法. 一.创建索引 (1)使用create index # 1.创建普通索引 create index 索引名 on 表名 (列名[(限制索引长度) ...

  8. ES中的内置对象--jquery如何优化代码,少用$进行查找,减少查找次数的方法

  9. Python从零到壹丨图像增强及运算:图像掩膜直方图和HS直方图

    摘要:本章主要讲解图像直方图相关知识点,包括掩膜直方图和HS直方图,并通过直方图判断黑夜与白天,通过案例分享直方图的实际应用. 本文分享自华为云社区<[Python从零到壹] 五十二.图像增强及 ...

  10. JSTL概述和JSTL常用标签if

    JSTL概述 1.概念:JavaServlet Pages Tag Library(JSP标准标签库) 是由Apache组织提供的开源的免费的jsp标签 <标签> 2.作用:用于简化和替换 ...