标签:ref fft 经典算法 决定 重复 出现 lin rac tor
大厨最近接受了来自某个著名的烹饪学校的教职。这份工作还没有正式开始,所以大厨打算利用剩下的时间好好地度个假。有\(N\)座城市(编号\(1 ~ N\)),由\(M\)条道路相连。对每个合法的\(i\),第\(i\)座城市内有\(L_i\)个旅游景点。大厨现在在城市\(1\),他将会在城市\(N\)教书。在他度假的每一天,他会进行如下活动中的某一种:
走到一个编号比他目前所在城市要高的城市,要求这个城市与他目前所在的城市之间有道路连接。在假期结束的时候,大厨必须在城市\(N\)。
访问一个他目前所在城市中的旅游景点。大厨可以(在不同的时间)重复访问同一个旅游景点。
大厨还没有决定度多久的假。他有\(Q\)个询问,由序列\(D_1, D_2, . . . , D_Q\)描述。对每个询问(也就是对每个\(i\),其中\(1 ≤ i ≤ Q\)),他希望知道如果他的假期恰好长\(D_i\)天的话,不同的可能的度假计划的个数。由于这个数可能非常大,请你求出它模\(1, 000, 000, 007\)的值。
我们认为两个(持续时间相同的)度假计划不同,如果存在某一天使得大厨在这两个计划中做的事情不一样。访问两个不同的旅游景点也算不一样的事情。
\(1 ≤ N ≤ 4, 000\),\(1 ≤ M ≤ 10^5\),\(1 ≤ Q ≤ 500\)。
https://blog.csdn.net/qq_38609262/article/details/105586345
设\(F_i(x)\)表示到达点ii的方案数关于时间的生成函数,显然有\(F_1(x)=\frac{1}{1-L_1x}\),\(F_i(x)=\frac{x\cdot\sum_{(j,i)\in E}F_j(x)}{1-L_ix}\)(\(i>1\))。那么可以发现\(F_N(x)\)可以写成\(\frac{P(x)}{Q(x)}\)?的形式,其中\(\text{deg}(P(x))< N\),\(Q(x)=\prod_{i=1}^{N}(1-L_ix)\)。至于求出\(P(x)\),可以考虑先DP出\(F_N(x)\)的前\(N\)项系数,乘上\(Q(x)\)即可,DP时间复杂度为\(\mathcal O(NM)\)。
现在每个询问即为给定\(k\),求出\([x^k]F_N(x)\)。如果直接用多项式取模加速常系数线性递推的经典算法,不仅单个询问复杂度为\(O(N\log N\log k)\),且因为模数不是NTT模数,需要任意模数FFT,因此常数非常大,不能通过。
可以发现现在给定了\(Q(x)\)的因子分解,可以尝试得到复杂度更优秀的算法。
考虑给\(L_i\)?排序,令去重后得到长度为\(d\)的数列\(p\),其中\(p_i\)?在\(L\)中出现了\(r_i\)?次。根据有理生成函数的一般展开定理,我们可以将\(F_N(x)\)表示为\(\sum_{i=1}^{d}\sum_{j=1}^{r_i}\frac{a_{i,j}}{(1-p_ix)^j}\)??,那么\([x^k]F_N(x)=\sum_{i=1}^{d}\sum_{j=1}^{r_i}a_{i,j}\cdot \binom{k+j-1}{j-1}\cdot {p_i}^k\)。这样只需要快速幂就可以在\(\mathcal O(N\log k)\)的时间复杂度内处理单个询问。
现在难点在于得到这个分解,也即得到每个常数\(a_{i,j}\)?。令\(R_i(x)=\prod_{j\neq i}(1-p_jx)^{r_j}\)?,注意到通分后有\(P(x)=\sum_{i=1}^{d}\sum_{j=1}^{r_i}(a_{i,j}\cdot R_i(x)\cdot (1-p_ix)^{r_i-j})\)。那么有\(P(\frac{1}{p_i})=a_{i,r_i}\cdot \prod_{j\neq i}(1-\frac{p_j}{p_i})^{r_j}\)?,于是容易得到\(a_{i,r_i}\)??。得到\(a_{i,r_i}\)?后从\(P(x)\)中减去对应项并整体除去一个\((1-p_ix)\)即可将\(r_i\)?减去\(1\),继续计算即可。这样分解时间复杂度为\(\mathcal O(N^2)\)。
总时间复杂度为\(\mathcal O(NM+N^2+QN\log V)\)。
CO int N=4e3+10;
int fac[N],ifac[N],w[N];
vector<int> to[N];
int f[N],p[N],q[N];
pair<int,int> buc[N];
vector<int> a[N];
int main(){
int n=read<int>(),m=read<int>(),Q=read<int>();
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
ifac[n]=fpow(fac[n],mod-2);
for(int i=n-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
for(int i=1;i<=n;++i) read(w[i]);
for(int i=1;i<=m;++i){
int x=read<int>(),y=read<int>();
to[x].push_back(y);
}
f[1]=1,p[0]=n==1;
for(int i=1;i<n;++i){
for(int x=n;x>=1;--x){
for(int y:to[x]) f[y]=add(f[y],f[x]);
f[x]=mul(f[x],w[x]);
}
p[i]=f[n];
}
q[0]=1;
for(int i=1;i<=n;++i)for(int j=n;j>=1;--j){
p[j]=add(p[j],mul(p[j-1],mod-w[i]));
q[j]=add(q[j],mul(q[j-1],mod-w[i]));
}
p[n]=0;
sort(w+1,w+n+1);
int num=0;
for(int l=1,r;l<=n;l=r+1){
for(r=l;r+1<=n and w[r+1]==w[l];++r);
buc[++num]={w[l],r-l+1};
}
int all=n;
for(int k=1;k<=num;++k){
int w=buc[k].first,c=buc[k].second;
int inv=fpow(w,mod-2);
for(int i=1;i<=c;++i){
int up=all-i+1;
for(int j=up;j>=1;--j){
q[j]=mul(q[j],mod-inv);
q[j-1]=add(q[j-1],mod-q[j]);
}
for(int j=0;j<=up-1;++j) q[j]=q[j+1];
q[up]=0;
}
all-=c;
int sr=0;
for(int i=all;i>=0;--i) sr=add(mul(sr,inv),q[i]);
sr=fpow(sr,mod-2);
a[k].resize(c+1);
for(int i=c;i>=1;--i){
int up=all-1+i,sl=0;
for(int j=up;j>=0;--j) sl=add(mul(sl,inv),p[j]);
a[k][i]=mul(sl,sr);
for(int j=0;j<=all;++j) p[j]=add(p[j],mod-mul(q[j],a[k][i]));
for(int j=up;j>=1;--j){
p[j]=mul(p[j],mod-inv);
p[j-1]=add(p[j-1],mod-p[j]);
}
for(int j=0;j<=up-1;++j) p[j]=p[j+1];
p[up]=0;
}
}
while(Q--){
int64 t=read<int64>();
int ans=0;
for(int k=1;k<=num;++k){
int w=buc[k].first,c=buc[k].second;
int pwr=fpow(w,t%(mod-1)),fac=1;
for(int i=1;i<=c;++i){
ans=add(ans,mul(a[k][i],mul(fac,mul(ifac[i-1],pwr))));
fac=mul(fac,(t+i)%mod);
}
}
write(ans,‘\n‘);
}
return 0;
}
标签:ref fft 经典算法 决定 重复 出现 lin rac tor
原文地址:https://www.cnblogs.com/autoint/p/13377729.html