一种类似等比数列求和问题的解法

有时,会遇到这样的问题:求 \(\sum_{i=1}^n i^kx^i\)
其中,k很小,n很大,x可以是数,矩阵,或多项式。
通常,有两种做法:

  1. 将x放入矩阵中,并依次把\((a+1)^i\)拆开,把系数放入矩阵(其实就是杨辉三角)。
    这个方法比较容易,但时间复杂度为\(O(k^3\log n)\)

  2. 使用递归。从\(\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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值