一种类似等比数列求和问题的解法
有时,会遇到这样的问题:求 \(\sum_{i=1}^n i^kx^i\)。
其中,k很小,n很大,x可以是数,矩阵,或多项式。
通常,有两种做法:
-
将x放入矩阵中,并依次把\((a+1)^i\)拆开,把系数放入矩阵(其实就是杨辉三角)。
这个方法比较容易,但时间复杂度为\(O(k^3\log n)\)。 -
使用递归。从\(\frac n 2\)的答案推到\(n\)的答案。
根据n的奇偶性分类讨论。
递归时维护0~k的所有答案。代码比较难写,注意细节。
时间复杂度\(O(k^2\log n)\)。
例题:
这道题比较卡常,需要使用方法二。
代码:
#include <stdio.h>
#define md 1000000007
void fuz(int a[102][102],int b[102][102],int n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
a[i][j]=b[i][j];
}
}
void chf(int a[102][102],int b[102][102],int n)
{
int jg[102][102]={0};
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(a[i][j])
{
for(int k=1;k<=n;k++)
jg[i][k]=(jg[i][k]+1ll*a[i][j]*b[j][k])%md;
}
}
}
fuz(a,jg,n);
}
void chf(int a[102][102],int x,int n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
a[i][j]=1ll*a[i][j]*x%md;
}
}
void add(int a[102][102],int b[102][102],int n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
a[i][j]=(a[i][j]+b[i][j])%md;
}
}
int G[102][102],A[102][102],B[102][102],C[102][102],mi[102][102],ag[102][102],bg[102][102],cg[102][102],jh[102][102];
void dfs(int m,int n)
{
if(m==1)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
mi[i][j]=A[i][j]=B[i][j]=C[i][j]=G[i][j];
}
return;
}
if(m%2==0)
{
int z=m/2;dfs(z,n);
fuz(ag,A,n);
chf(ag,mi,n);
add(A,ag,n);
fuz(bg,B,n);
chf(bg,mi,n);
add(B,bg,n);
chf(ag,z,n);
add(B,ag,n);
fuz(cg,C,n);
chf(cg,mi,n);
add(C,cg,n);
chf(bg,z*2,n);
add(C,bg,n);
chf(ag,z,n);
add(C,ag,n);
chf(mi,mi,n);
}
else
{
int z=m/2;dfs(z,n);
fuz(jh,mi,n);z+=1;
chf(mi,G,n);
fuz(ag,A,n);
for(int i=1;i<=n;i++)
ag[i][i]=(ag[i][i]+1)%md;
chf(ag,mi,n);
add(A,ag,n);
fuz(bg,B,n);
chf(bg,mi,n);
add(B,bg,n);
chf(ag,z,n);
add(B,ag,n);
fuz(cg,C,n);
chf(cg,mi,n);
add(C,cg,n);
chf(bg,z*2,n);
add(C,bg,n);
chf(ag,z,n);
add(C,ag,n);
chf(mi,jh,n);
}
}
int main()
{
int n,m,k,q;
scanf("%d%d%d%d",&n,&m,&k,&q);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
G[a][b]+=1;
}
dfs(k,n);
for(int i=0;i<q;i++)
{
int s,t;
scanf("%d%d",&s,&t);
printf("%d\n",C[s][t]);
}
return 0;
}