题目描述

  问有多少个满足以下要求的\(k\)进制数:

   1.每个数字出现的次数不超过\(n\)

   2.\(0\)没有出现过

   3.若\(g_{i,j}=0\),则\(i\)不能出现恰好\(j\)次。

  两次询问之间会修改\(g\)中一个位置的值(\(0\)变\(1\)或\(1\)变\(0\))。

  输出所有询问的答案的和。

  \(3\leq k\leq 10,n\leq 14000,m\leq 20\)

  模数\(p=786433\),原根\(g=10\)

题解

  假设第\(i\)个数用了\(c_i\)个,答案为

\[\frac{(\sum c_i)!}{\prod c_i!}
\]

  构造多项式

\[f_i(x)=\sum_{j=0}^n\frac{g_{i,j}}{j!}x^j
\]

  把这\(k-1\)个多项式乘起来后,第\(i\)项乘以\(i!\)的和就是答案。

  因为求的是答案的和,所以可以在点值表达的形式下累加答案,最后IDFT回来。

​ 怎么求没修改前的答案?

  直接DFT

  怎么求修改的贡献?

  观察NTT的公式:

\[y_k=\sum_{j=0}^{n-1}a_j{(g^\frac{p-1}{n})}^{kj}
\]

  对于一个单点修改操作,可以看成在某个多项式上加上一个只有一项系数不为\(0\)的多项式。这个多项式DFT后就是一个等比数列,直接加到原多项式上就完了。

  对于所有多项式的乘积:如果所有多项式的每一项都非\(0\),就直接乘以逆元。现在有\(0\),就记录每一项\(0\)的个数和非\(0\)的乘积。

  时间复杂度:\(O(nk^2\log (nk)+mnk)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll p=786433;
const ll g=10;
ll inv[1000010];
ll fac[1000010];
ll ifac[1000010];
ll pg[1000010];
ll fp(ll a,ll b)
{
ll s=1;
while(b)
{
if(b&1)
s=s*a%p;
a=a*a%p;
b>>=1;
}
return s;
}
namespace ntt
{
int n;
ll w1[150000];
ll w2[150000];
int rev[150000];
void init(int x)
{
n=1;
while(n<=x)
n<<=1;
int i;
for(i=1;i<=n;i<<=1)
{
w1[i]=fp(g,(p-1)/i);
w2[i]=inv[w1[i]];
}
rev[0]=0;
for(i=1;i<n;i++)
rev[i]=(rev[i>>1]>>1)|(i&1?n>>1:0);
}
void ntt(ll *a,int t)
{
int i,j,k;
ll u,v,w,wn;
for(i=0;i<=n-1;i++)
if(rev[i]<i)
swap(a[i],a[rev[i]]);
for(i=2;i<=n;i<<=1)
{
wn=(t==1?w1[i]:w2[i]);
for(j=0;j<n;j+=i)
{
w=1;
for(k=j;k<j+i/2;k++)
{
u=a[k];
v=a[k+i/2]*w%p;
a[k]=(u+v)%p;
a[k+i/2]=(u-v)%p;
w=w*wn%p;
}
}
}
if(t==-1)
for(i=0;i<n;i++)
a[i]=a[i]*inv[n]%p;
}
}
int &nn=ntt::n;
char s[14010];
int c[12][14010];
void init()
{
int i;
inv[0]=inv[1]=1;
for(i=2;i<=p-1;i++)
inv[i]=(-(p/i)*inv[p%i]%p+p)%p;
fac[0]=ifac[0]=1;
for(i=1;i<=p-1;i++)
{
fac[i]=fac[i-1]*i%p;
ifac[i]=ifac[i-1]*inv[i]%p;
}
}
ll ans;
ll d[12][150000];
ll f[150000];
ll f2[150000];
ll a[150000];
int k,n,m;
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
init();
int i,j;
scanf("%d%d%d",&k,&n,&m);
ntt::init((k-1)*n);
pg[0]=1;
for(i=1;i<=p-2;i++)
pg[i]=pg[i-1]*g%p;
for(i=1;i<=k-1;i++)
{
scanf("%s",s);
for(j=0;j<=n;j++)
c[i][j]=s[j]-'0';
}
ans=0;
for(i=0;i<nn;i++)
f[i]=1;
for(i=1;i<=k-1;i++)
{
ll *u=d[i];
for(j=0;j<nn;j++)
u[j]=0;
for(j=0;j<=n;j++)
u[j]=c[i][j]*ifac[j]%p;
ntt::ntt(u,1);
for(j=0;j<nn;j++)
{
if(u[j]<0)
u[j]+=p;
if(u[j])
f[j]=f[j]*u[j]%p;
else
f2[j]++;
}
}
for(i=0;i<nn;i++)
if(!f2[i])
a[i]=(a[i]+f[i])%p;
int x,y;
int t;
for(t=1;t<=m;t++)
{
scanf("%d%d",&x,&y);
c[x][y]^=1;
for(i=0;i<nn;i++)
if(d[x][i])
f[i]=f[i]*inv[d[x][i]]%p;
else
f2[i]--;
ll s1=pg[((p-1)/nn*y)%(p-1)],s2=ifac[y];
if(!c[x][y])
s2=p-s2;
for(i=0;i<nn;i++)
{
d[x][i]+=s2;
if(d[x][i]>=p)
d[x][i]-=p;
s2=s2*s1%p;
}
for(i=0;i<nn;i++)
{
if(d[x][i])
f[i]=f[i]*d[x][i]%p;
else
f2[i]++;
if(!f2[i])
a[i]=(a[i]+f[i])%p;
}
}
ntt::ntt(a,-1);
for(i=1;i<nn;i++)
ans=(ans+a[i]*fac[i])%p;
ans=(ans%p+p)%p;
printf("%lld\n",ans);
return 0;
}

【XSY2535】整数 NTT的更多相关文章

  1. [学习笔记&教程] 信号, 集合, 多项式, 以及各种卷积性变换 (FFT,NTT,FWT,FMT)

    目录 信号, 集合, 多项式, 以及卷积性变换 卷积 卷积性变换 傅里叶变换与信号 引入: 信号分析 变换的基础: 复数 傅里叶变换 离散傅里叶变换 FFT 与多项式 \(n\) 次单位复根 消去引理 ...

  2. Tsinsen A1493 城市规划(DP + CDQ分治 + NTT)

    题目 Source http://www.tsinsen.com/A1493 Description 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在 ...

  3. NTT

    1 问题描述FFT问题解决的是复数域上的卷积.如果现在的问题是这样:给出两个整数数列$Ai,Bj,0\leq i\leq n-1,0\leq j\leq m-1$,以及素数$P$,计算新数列$Ci=( ...

  4. 卷积FFT、NTT、FWT

    先简短几句话说说FFT.... 多项式可用系数和点值表示,n个点可确定一个次数小于n的多项式. 多项式乘积为 f(x)*g(x),显然若已知f(x), g(x)的点值,O(n)可求得多项式乘积的点值. ...

  5. FFT\NTT总结

    学了好久,终于基本弄明白了 推荐两个博客: 戳我 戳我 再推荐几本书: <ACM/ICPC算法基础训练教程> <组合数学>(清华大学出版社) <高中数学选修> 预备 ...

  6. 快速傅里叶变换FFT& 数论变换NTT

    相关知识 时间域上的函数f(t)经过傅里叶变换(Fourier Transform)变成频率域上的F(w),也就是用一些不同频率正弦曲线的加 权叠加得到时间域上的信号. \[ F(\omega)=\m ...

  7. 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/常用套路【入门】

    原文链接https://www.cnblogs.com/zhouzhendong/p/Fast-Fourier-Transform.html 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/ ...

  8. 【2019雅礼集训】【CF 960G】【第一类斯特林数】【NTT&多项式】permutation

    目录 题意 输入格式 输出格式 思路 代码 题意 找有多少个长度为n的排列,使得从左往右数,有a个元素比之前的所有数字都大,从右往左数,有b个元素比之后的所有数字都大. n<=2*10^5,a, ...

  9. 快速数论变换(NTT)小结

    NTT 在FFT中,我们需要用到复数,复数虽然很神奇,但是它也有自己的局限性--需要用double类型计算,精度太低 那有没有什么东西能够代替复数且解决精度问题呢? 这个东西,叫原根 原根 阶 若\( ...

随机推荐

  1. python第一章:简介与安装--小白博客

    Python简介 Python是一种计算机程序设计语言.是一种动态的.面向对象的脚本语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越来越多被用于独立的.大型项 ...

  2. 莫比乌斯反演III

    "haik, hen wir." -- somebody 概述 莫比乌斯反演通过一些恒等变形使需要高时间复杂度计算的式子变为可快速计算的. 一般来说,将形如\(\sum_{d|n} ...

  3. c++ 入门之深入探讨拷贝函数和内存分配

    在c++入门之深入探讨类的一些行为时,说明了拷贝函数即复制构造函数运用于如下场景: 对象作为函数的参数,以值传递的方式传给函数. 对象作为函数的返回值,以值的方式从函数返回 使用一个对象给另一个对象初 ...

  4. C#对摄像头的操作示例,采用Aforge库

    操作摄像头有三个办法:VFW.DirectShow.花钱买第三方控件 VFW技术比较古老,无法解决驱动不完善造成的某些问题 DirectShow技术相对完善一些,但这是C++才能实现的技术.如果用.N ...

  5. Masonry练习详解

    添加约束的方式: 1.通过使用NSLayoutConstraints添加约束到约束数组中,之前必须设置translatesAutoresizingMaskIntoConstraints = NO,即取 ...

  6. 容器化-Docker介绍

    导读:本文章对Docker技术进行了介绍,阐述了Docker的技术发展历程.容器与虚拟机的差异.Docker原理.特点.Docker三组件和Docker带来的影响,为我们进一步理解Docker打下基础 ...

  7. html总结:float实现span和input输入框同行

    例: <input type="text" name="ytdwname" value="<%=user.getYtdwname() %& ...

  8. [转帖]关于CP936

    来源: 知乎:https://www.zhihu.com/question/35609295/answer/63780022 CP936和UTF-8本身和Python是毫无关联的. CP936其实就是 ...

  9. 牛客练习赛13E 乌龟跑步

    题目链接:https://ac.nowcoder.com/acm/contest/70/E 题目大意: 略 分析: DP或记忆化搜索,个人觉得记忆化搜索比较好做,逻辑清晰,代码量少 代码如下: #in ...

  10. python之路--操作系统介绍,进程的创建

    一 .  操作系统的作用: 1:隐藏丑陋复杂的硬件接口,提供良好的抽象接口 2:管理.调度进程,并且将多个进程对硬件的竞争变得有序 二 多道技术: 所谓多道程序设计技术,就是指允许多个程序同时进入内存 ...