hdu 6395 Sequence(矩阵运算模板+简单暴力)

Sequence

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1181    Accepted Submission(s): 431


 

Problem Description

Let us define a sequence as below

F_{1}=A

F_{2}=B

F_{n}=C*F_{n-2}+D*F_{n-1}+\left \lfloor \frac{P}{n} \right \rfloor



  Your job is simple, for each task, you should output Fn module 109+7.

 

Input

The first line has only one integer T, indicates the number of tasks.

Then, for the next T lines, each line consists of 6 integers, A , B, C, D, P, n.

1≤T≤200≤A,B,C,D≤1091≤P,n≤109

 

Sample Input

 

2

3 3 2 1 3 5

3 2 2 2 1 4

 

Sample Output

 

36

24

 题意:

如上图公式所示,让你求出第n项

解析:

首先讲大佬的方法吧,我自己的方法可能有点麻烦.......不过核心思想是一样的——枚举\left \lfloor \frac{P}{n} \right \rfloor

因为这一项,好像总共只有\sqrt{P}项,所以我们就可以发现,在一段连续的区间内的F_{i}的常数项\left \lfloor \frac{P}{i} \right \rfloor是相等的。

如果是相等的话,我们就可以用一个矩阵求出这一段区间里面的任意一项。

那么对于这一段段连续的常数相等的区间,我们就可以一步步递推下去。

s1=\begin{bmatrix} F_{i} &F_{i+1} & x \end{bmatrix}

s2=\begin{bmatrix} 0 & C &0 \\ 1 & D &0 \\ 0& 1 & 1 \end{bmatrix}

构造两个矩阵这里枚举每一段区间的时候,就让x=这一段区间的常数项的值,

然后再让s1*s2,就可以算出该区间任意的一个值 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

 

typedef long long ll;
const int MOD = 1e9+7;
const int MAXN = 40000;

typedef struct {

	int m[4][4];
	int sizx,sizy;

}Matrix;
int n,m;

int read(){
 
    char c=getchar();int x=0,f=1;
 
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
 
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
 
    return x*f;
 
}



inline Matrix Mul(Matrix a, Matrix b)

{

    Matrix c;

    memset(c.m, 0, sizeof(c.m));
	c.sizx=a.sizx;
	c.sizy=b.sizy;

	for (int i = 0; i < a.sizx; i++)
    {

		for (int j = 0; j < b.sizy; j++)

        {

			for (int k = 0; k < a.sizy; k++)

            {

                c.m[i][j] = (c.m[i][j] + (1ll * a.m[i][k] * b.m[k][j])%MOD  )%MOD ;

            }

        }

    }

    return c;

}

inline Matrix fastm(Matrix a, ll num)
{

    Matrix res;

    memset(res.m, 0, sizeof(res.m));
	res.sizx=a.sizx;
	res.sizy=a.sizy;
	for(int i=0;i<a.sizx;i++)
        res.m[i][i]=1;
    while (num)

    {

        if (num & 1)

            res = Mul(res, a);

        num >>= 1;

        a = Mul(a, a);

    }

    return res;

}

inline Matrix ReZero()
{
	Matrix zero;
		zero.m[0][0]=0;
		zero.m[0][1]=zero.m[0][2]=0;
		zero.m[1][0]=0;
		zero.m[1][1]=zero.m[1][2]=0;
		zero.m[2][0]=zero.m[2][1]=0;
		zero.m[2][2]=0;
		zero.sizx=zero.sizy=3;
		return zero;
}
int prime[MAXN];
int tot=0;
int main()
{
	int t;
	//scanf("%d",&t);
	t=read();
	int A,B,C,D,P;
	while(t--)
	{
		//scanf("%lld%lld%lld%lld%lld%d",&A,&B,&C,&D,&P,&n);
		//scanf("%d%d%d%d%d%d",&A,&B,&C,&D,&P,&n);
		A=read();
		B=read();
		C=read();
		D=read();
		P=read();
		n=read();

		if(n==1)
		{
			printf("%d\n",A);
			continue;
		}
		if(n==2)
		{
			printf("%d\n",B);
			continue;
		}
		int res=0;
		Matrix s1;
		s1.m[0][0]=A;
		s1.m[0][1]=B;
		s1.m[0][2]=0;
		s1.sizx=1;
		s1.sizy=3;

		Matrix b;
		b.m[0][0]=0;
		b.m[0][1]=C;
		b.m[0][2]=0;
		b.m[1][0]=1;
		b.m[1][1]=D;
		b.m[1][2]=0;
		b.m[2][0]=0;
		b.m[2][1]=b.m[2][2]=1;
		b.sizx=b.sizy=3;

		for(int i=3;i<=n;)
		{
			int ans=P/i;
			if(ans==0)
			{
				s1.m[0][2]=ans;
				Matrix s2=fastm(b,n-i+1);
				s1=Mul(s1,s2);
				break;
			}
			int w=P/ans;
			w=w>n?n:w;
			s1.m[0][2]=ans;
			Matrix s2=fastm(b,w-i+1);
			s1=Mul(s1,s2);
			i=w+1;
		}

		printf("%d\n",s1.m[0][1]);
				
	}
	return 0;
	

}

下面就是我自己的方法了.......

我是把常数和前面的那项分出来的

F_{n}=Q_{n}+K_{n}

Q_{n}=C*Q_{n-2}+D*Q_{n-1}

K_{n}=C*K_{n-2}+D*K_{n-1}+\left \lfloor \frac{P}{n} \right \rfloor

Qn是可以用矩阵快速幂算的

后面那项我枚举了几个

K_{6}=(D^3+2CD)*\left \lfloor \frac{P}{3} \right \rfloor+(D^2+C)*\left \lfloor \frac{P}{4} \right \rfloor+D*\left \lfloor \frac{P}{5} \right \rfloor+\left \lfloor \frac{P}{6} \right \rfloor

我发现了前一项的系数,是由后面两项相乘得到了,即(D^2+C)*D+D*C=(D^3+2CD)

之后的每一项也可以由这个规律推出

那么我们就可以写出Kn的表达式了

K_{n}=X_{1}*\left \lfloor \frac{P}{n} \right \rfloor+X_{2}*\left \lfloor \frac{P}{n-1} \right \rfloor+(X_{3})*\left \lfloor \frac{P}{n-2} \right \rfloor+(X_{4})*\left \lfloor \frac{P}{n-3} \right \rfloor+...+(X_{n-2})*\left \lfloor \frac{P}{3} \right \rfloor

X_{1}=1

X_{2}=D

X_{3}=(D^2+C)

...

X_{n}=C*X_{n-2}+D*X_{n-1}

这样我们就可以用矩阵快速幂来求Kn了,同样是上面的思想,一段段递推上去,

因为这里有求和,因为这些值我们是求和的,所以构造的矩阵为

s1=\begin{bmatrix} X_{i} &X_{i+1} & sum \end{bmatrix} 

s2=\begin{bmatrix} 0 & C &C\\ 1 & D &D \\ 0& 0 & 1 \end{bmatrix}

sum为X的区间和,即先求出一段区间的系数和,再把他们乘以常数项,就是Kn的一段区间的值

注意这里要一步步递推上去,对于区间[j,w]不能用类似前缀和的思想[w,1]-[j-1,1]来求,会T.....

然后我这里把j==w的情况也单独拿出来算,避免使用快速幂消耗时间...所以常数优化一波后跑了0.7s。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

 

typedef long long ll;
const int MOD = 1e9+7;
const int MAXN = 40000;

typedef struct {

	int m[4][4];
	int sizx,sizy;

}Matrix;
int n,m;

int read(){
 
    char c=getchar();int x=0,f=1;
 
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
 
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
 
    return x*f;
 
}



inline Matrix Mul(Matrix a, Matrix b)

{

    Matrix c;

    memset(c.m, 0, sizeof(c.m));
	c.sizx=a.sizx;
	c.sizy=b.sizy;

	for (int i = 0; i < a.sizx; i++)
    {

		for (int j = 0; j < b.sizy; j++)

        {

			for (int k = 0; k < a.sizy; k++)

            {

                c.m[i][j] = (c.m[i][j] + (1ll * a.m[i][k] * b.m[k][j])%MOD  )%MOD ;

            }

        }

    }

    return c;

}

inline Matrix Del(Matrix a, Matrix b)
{
	Matrix c;

    //memset(c.m, 0, sizeof(c.m));
	c.sizx=a.sizx;
	c.sizy=a.sizy;
    for (int i = 0; i < a.sizx; i++)
    {

        for (int j = 0; j < a.sizy; j++)
        {

			c.m[i][j]=(a.m[i][j]-b.m[i][j]+MOD)%MOD;

        }

    }

    return c;
}

 

inline Matrix fastm(Matrix a, ll num)

{

    Matrix res;

    memset(res.m, 0, sizeof(res.m));
	res.sizx=a.sizx;
	res.sizy=a.sizy;
	for(int i=0;i<a.sizx;i++)
        res.m[i][i]=1;
    while (num)

    {

        if (num & 1)

            res = Mul(res, a);

        num >>= 1;

        a = Mul(a, a);

    }

    return res;

}

inline Matrix ReZero()
{
	Matrix zero;
		zero.m[0][0]=0;
		zero.m[0][1]=zero.m[0][2]=0;
		zero.m[1][0]=0;
		zero.m[1][1]=zero.m[1][2]=0;
		zero.m[2][0]=zero.m[2][1]=0;
		zero.m[2][2]=0;
		zero.sizx=zero.sizy=3;
		return zero;
}
int prime[MAXN];
int tot=0;
int main()
{
	int t;
	//scanf("%d",&t);
	t=read();
	int A,B,C,D,P;
	while(t--)
	{
		//scanf("%lld%lld%lld%lld%lld%d",&A,&B,&C,&D,&P,&n);
		//scanf("%d%d%d%d%d%d",&A,&B,&C,&D,&P,&n);
		A=read();
		B=read();
		C=read();
		D=read();
		P=read();
		n=read();

		if(n==1)
		{
			printf("%d\n",A);
			continue;
		}
		if(n==2)
		{
			printf("%d\n",B);
			continue;
		}
		Matrix a,b;
		a.m[0][0]=0;
		a.m[0][1]=C;
		a.m[1][0]=1;
		a.m[1][1]=D;
		a.sizx=a.sizy=2;
		Matrix Q;
		Q.m[0][0]=A;
		Q.m[0][1]=B;
		Q.sizx=1;
		Q.sizy=2;
		a=fastm(a,n-2);
		Q=Mul(Q,a);   //算Qn
		b.m[0][0]=0;
		b.m[0][1]=b.m[0][2]=C;
		b.m[1][0]=1;
		b.m[1][1]=b.m[1][2]=D;
		b.m[2][0]=b.m[2][1]=0;
		b.m[2][2]=1;

		b.sizx=b.sizy=3;

		

		Matrix K;   //算Kn
		K.m[0][0]=0;
		K.m[0][1]=1;
		K.m[0][2]=1;
		K.sizx=1;
		K.sizy=3;
		int j=3;
		int res=0;
		int ans=P;
		int ck=0;
		tot=0;
		while(ans>=1&&j<=n)   //找一开始j==w的区间
		{
			ans=P/j;
			if(ans==0) break; 
			int w=P/ans;
			if(j==w)
			{
				ck=j;
				prime[ck]=ans;

			}
			else
			{
				break;
			}
			j=w+1;
		}
		Matrix tb;
		tb.m[0][0]=0;
		tb.m[0][1]=C;
		tb.m[1][0]=1;
		tb.m[1][1]=D;
		tb.sizx=tb.sizy=2;

		Matrix  tK;
		tK.m[0][0]=0;
		tK.m[0][1]=1;
		tK.sizx=1;
		tK.sizy=2;

		if(ck>=3)  //算j==w的区间的和
		{
			Matrix sc;
			Matrix nb=fastm(tb,n-ck);
			sc=Mul(tK,nb);
			int wan=prime[ck];
			res=(res+1ll*sc.m[0][1]*wan%MOD)%MOD;
			for(int i=ck-1;i>=3;i--)
			{
			sc=Mul(sc,tb);
			int wan=prime[i];
			res=(res+1ll*sc.m[0][1]*wan%MOD)%MOD;
			}
		}



		Matrix s1,s2;
		int k=P>=n?n:P;
		s1=K;
		if(n>P)
		{
			s2=fastm(b,n-P);
			s1=Mul(K,s2);
			s1.m[0][2]=s1.m[0][1];
		}
		while(ans>=1&&k>=j)
		{
			ans=P/k;
			if(ans==0) break; 
			int w=(P/(ans+1))+1;
			if(w<3) w=3;
			s2=fastm(b,k-w);
			s1=Mul(s1,s2);
			res=(res+1ll*s1.m[0][2]*ans%MOD)%MOD;
			s1=Mul(s1,b);
			s1.m[0][2]=s1.m[0][1];
			k=w-1;			
		}
		
		
		
		res=(res+Q.m[0][1])%MOD;
		printf("%d\n",res);
		
	}
	return 0;
	

}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/81667503
今日推荐