HDU_Steps8.3 矩阵应用 HDU1575 HDU1757 HDU2294 HDU2254 HDU2276 HDU2855 HDU3519 HDU3509

STEPS8.3一些矩阵的应用,还是比较难的

HDU1575 Tr A

矩阵相乘基础题

HDU1757 A Simple Math Problem

7.2中的题目,重复了,构造矩阵

HDU2294 Pendant

f[i][j]代表j种颜色组成i个珠子,有方程f[i][j]=f[i-1][j]*j+f[i-1][j-1]*(k-j-1),但由于N较大采用一般的方法DP必然按会超时的。考虑构造矩阵f[i]=A*f[i-1],然后允许的长度是k~n,所以ans=(A^k+....+A^n)*f[0],初始只有f[0][0]=1,其它都是0.所以答案是矩阵种的a[k][0]

#include<cstdio>
#include<string.h>
using namespace std;
int cas,n,k;
const int MOD=1234567891;
typedef long long LL;
struct mat{
	int a[31][31];
	mat(int type){
		memset(a,0,sizeof a);	
		if(type==1){
			for(int i=0;i<=k;i++){
				a[i][i]=i;
				if(i>0)a[i][i-1]=k-i+1;
			}	
		}
	}
	mat mult(mat b){//矩阵加法
		mat c(0);
		for(int i=0;i<=k;i++)
			for(int j=0;j<=k;j++)
				for(int l=0;l<=k;l++)
					c.a[i][j]=((LL)c.a[i][j]+(LL)a[i][l]*(LL)b.a[l][j])%MOD;
		return c;			
	}
	mat add(mat b){//矩阵乘法
		mat c(0);
		for(int i=0;i<=k;i++)
			for(int j=0;j<=k;j++)	
				c.a[i][j]=((LL)a[i][j]+(LL)b.a[i][j])%MOD;
		return c;
	}
};
//二分计算A^k
mat binMat(int k){
	if(k<=1)return mat(k);
	mat m=binMat(k/2);
	m=m.mult(m);
	if(k&1)m=m.mult(mat(1));
	return m;
}
//二分计算A^l+...A^r
mat cal(int l,int r){
	if(l==r)return binMat(l);
	int m=(l+r+1)/2;	
	mat t=cal(l,m-1);//算前一半的和
	mat t2=binMat(m-l);//中介的矩阵
	t=t.add(t2.mult(t));
	if((r-l+1)&1)t=t.add(binMat(r));//如果是奇数,则加上最后一个
	return t;
}

int main(){
	scanf("%d",&cas);
	while(cas--){
		scanf("%d%d",&n,&k);		
		printf("%d\n",cal(k,n).a[k][0]);
		
		
	}
	return 0;	
}

HDU2254 奥运

路径条数,只要把邻接矩阵乘起来就可以了。数据比较小,先打表,再再表中直接取就行了

#include<cstdio>
#include<map>
#include<string.h>
using namespace std;

int cas,n,v1,v2,t1,t2,ta,tb,ncity;
int m[32][32];//标记初始矩阵
map<int,int> mmap;
struct jz{
	int a[32][32];
	jz(){}
	jz(int type){
		if(type==0)memset(a,0,sizeof a);
		if(type==1){
			for(int i=1;i<=ncity;i++)for(int j=1;j<=ncity;j++)a[i][j]=m[i][j];	
		}
	}
	jz mult(jz jb){
		jz jc(0);
		for(int i=1;i<=ncity;i++){
			for(int j=1;j<=ncity;j++){
				for(int k=1;k<=ncity;k++){
					jc.a[i][j]=(jc.a[i][j]+a[i][k]*jb.a[k][j])%2008;	
				}	
			}	
		}
		return jc;
	}
}M[10001];
int main(){
	while(scanf("%d",&n)!=EOF){
		mmap.clear();
		memset(m,0,sizeof m);
		ncity=1;
		
		for(int i=1;i<=n;i++){
			scanf("%d%d",&ta,&tb);
			if(mmap.count(ta)==0)mmap[ta]=ncity++;
			if(mmap.count(tb)==0)mmap[tb]=ncity++;
			m[mmap[ta]][mmap[tb]]++;
		}
		
		memcpy(M[0].a,m,sizeof m);
		for(int i=1;i<=10000;i++)M[i]=M[i-1].mult(jz(1));
		
		scanf("%d",&n);
		while(n--){
			int ans=0;
			scanf("%d%d%d%d",&v1,&v2,&t1,&t2);
			if(mmap.count(v1)==0||mmap.count(v2)==0){
				ans=0;	
			}else{
				for(int i=t1-1;i<t2;i++){
					ans=(ans+M[i].a[mmap[v1]][mmap[v2]])%2008;	
				}	
				
			}
			printf("%d\n",ans);
			
		}
	}
	return 0;	
}


HDU2276 Kiki & Little Kiki 2

构造矩阵,第i个灯的状态d[i],以及它左边的灯的状态d[i-1],则下一秒d[i]=(d[i]+d[i-1])/2,一共就4种情况01,10,00,10,模拟一下就可以验证方程的正确性了,然后利用矩阵可以再logN内得到第N秒的状态,注意这里不能用递归二分矩阵,会暴栈

#include<cstdio>
#include<string.h>
using namespace std;
int n,m;
char s[105];
struct jz{
	int a[101][101];
	jz(int type){
		memset(a,0,sizeof a);
		if(type==1){
			for(int i=1;i<=n;i++){
				a[i][i==1?n:i-1]=a[i][i]=1;	
			}
		}
		if(type==2){//单位矩阵
			for(int i=1;i<=n;i++)a[i][i]=1;	
		}
	}	
	jz mult(jz b){
		jz c(0);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				for(int k=1;k<=n;k++){
					c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j]);
				}
				c.a[i][j]%=2;//模操作是很占时间的!!
			}
		}
		return c;
	}
};
void outres(jz jj){
	int rs[105];
	memset(rs,0,sizeof rs);
	for(int i=1;i<=n;i++){
		for(int k=1;k<=n;k++){
			rs[i]=rs[i]+jj.a[i][k]*(s[k-1]-'0');
		}
		rs[i]%=2;
	}
	
	for(int i=1;i<=n;i++)printf("%d",rs[i]);
	printf("\n");
}
//二分矩阵的非递归写法
jz binMat(int k){
	jz tmp(2),m(1); 
	while(k){
		if(k&1)tmp=tmp.mult(m);
		m=m.mult(m);	
		k=k>>1;
	}
	return tmp;
}
int main(){
	while(scanf("%d",&m)!=EOF){
		scanf("%s",s);
		n=strlen(s);

		jz r=binMat(m);		
		outres(r);
	}
	return 0;	
}

HDU2855 Fibonacci Check-up 

求sigma(c(n,k)*F(k)),其中F是Fibo数列,很容易想到用牛顿二项式展开,Fibo数列可以表示乘A^k*[f0,f1]

#include<cstdio>
#include<string.h>
using namespace std;
int cas,n,m;
/*(|1 0| + |0 1|)^n
 * |0 1|   |1 1|
 * */
struct jz{
	int a[3][3];
	jz(int type){
		memset(a,0,sizeof a);
		if(type==1){
			a[1][1]=a[2][1]=a[1][2]=1;
			a[2][2]=2;
		}
	}
	jz mult(jz b){
		jz c(0);
		c.a[1][1]=(a[1][1]*b.a[1][1]+a[1][2]*b.a[2][1])%m;
		c.a[1][2]=(a[1][1]*b.a[1][2]+a[1][2]*b.a[2][2])%m;
		c.a[2][1]=(a[2][1]*b.a[1][1]+a[2][2]*b.a[2][1])%m;
		c.a[2][2]=(a[2][1]*b.a[1][2]+a[2][2]*b.a[2][2])%m;
		return c;
	}
};

jz binmat(int k){
	if(k<=1)return jz(k);
	jz r=binmat(k/2);
	r=r.mult(r);
	if(k&1)r=r.mult(jz(1));
	return r;
}

int main(){
	scanf("%d",&cas);
	while(cas--){
		scanf("%d%d",&n,&m);
		
		if(n<=1)
			printf("%d\n",n);
		else{
			jz r=binmat(n);
			printf("%d\n",r.a[1][2]);
		}
	}
	return 0;
}

HDU3519 Lucky Coins Sequence

一串硬币,至少有三个连续面相同的硬币的摆法有多少种,具体的解见注释

#include<cstdio>
#include<string.h>
using namespace std;
/* d[i]代表合法的个数
 * f[i][2]代表末两位相同
 * f[i][1]代表末两位不同
 * d[i]=2*d[i-1]+f[i-1][2]	   合法的加上 正反 都依然合法,或者末两位相同再加上一个相同的也会合法
 * f[i][2]=f[i-1][1]		   末两位不同加上与末位相同的可以使其末两位相同
 * f[i][1]=f[i-1][2]+f[i-1][1] 末两位相同或者不同 加上一个与末位不同的 都可以使其成为末两位不同的
 * 构造矩阵求解
 *  | 2 1 0 |   |d[i-1]   | |d[i]   |
 *  | 0 0 1 | * |f[i-1][2]|=|f[i][2]|
 *  | 0 1 1 |   |f[i-1][1]| |f[i][1]|
 */

struct mat{
	int a[4][4];
	mat(int type){
		memset(a,0,sizeof a);
		if(type==1){
			a[1][1]=2;
			a[1][2]=a[2][3]=a[3][2]=a[3][3]=1;	
		}	
	}
	mat mult(mat m){
		mat r(0);
		for(int i=1;i<=3;i++){
			for(int j=1;j<=3;j++){
				for(int k=1;k<=3;k++){
					r.a[i][j]=(r.a[i][j]+a[i][k]*m.a[k][j])%10007;
				}	
			}	
		}
		return r;
	}
};
mat binMat(int k){
	if(k<=1)return mat(k);
	mat m=binMat(k/2);
	m=m.mult(m);
	if(k&1)m=m.mult(mat(1));
	return m;	
}
int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		if(n<=2){
			printf("0\n");
			continue;
		}
		mat r=binMat(n-2);
		
		printf("%d\n",(2*r.a[1][2]+2*r.a[1][3])%10007);
	}
	
	return 0;	
}


HDU3509 Buge's Fibonacci Number Problem

给出 F1=f1,F2=f2,Fn=aFn-1+bFn-2

求sigma(Fi^k)..i=1~n (n<1e10,k<50)

很麻烦的一道题目,思想还是用牛顿二项式构造矩阵求解

#include<cstdio>
#include<string.h>
using namespace std;
typedef long long LL;
int cas,f1,f2,a,b,n,m,k;
int c[55][55],mpa[55],mpb[55],mpf1[55],mpf2[55];
int s1,s2;
/*这题要注意模的问题,一不小心就会越界
 *展开二项式,构造矩阵
 * 比如当k=3时,a=b=1时,矩阵如下
 * | 0 0 0 1 0 | |    f1^k    | |   f2^k     |
 * | 0 0 1 1 0 | | f2*f1^(k-1)| | f3*f2^(k-1)|
 * | 0 1 2 1 0 |×|    ..      |=|    ..      |
 * | 1 3 3 1 0 | |    f2^k    | |   f3^k     |
 * | 1 3 3 1 1 | |     s2     | |    s3      |
 */ 

void init(){//初始化系数数组,二项式,以及a^n,b^n,以及前两相和
	//系数
	c[1][1]=1;
	c[2][1]=c[2][2]=1;
	for(int i=3;i<=54;i++){
		c[i][1]=c[i][i]=1;
		for(int j=2;j<=i-1;j++)
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%m;	
	}
	//a,b的幂
	mpa[0]=mpb[0]=1;		
	for(int i=1;i<=54;i++){
		mpa[i]=((LL)mpa[i-1]*(LL)a)%m;
		mpb[i]=((LL)mpb[i-1]*(LL)b)%m;
	}
	//f1,f2的幂
	mpf1[0]=mpf2[0]=1;		
	for(int i=1;i<=54;i++){
		mpf1[i]=((LL)mpf1[i-1]*(LL)f1)%m;
		mpf2[i]=((LL)mpf2[i-1]*(LL)f2)%m;
	}
	//前两项
	s1=mpf1[k];
	s2=(mpf2[k]+mpf1[k])%m;
}
struct mat{
	int a[55][55];
	mat(int type){
		memset(a,0,sizeof a);
		if(type==1){
			a[k+1][k+1]=1;
			for(int i=0;i<=k+1;i++)
				for(int j=k,jj=0;j>=k-i;j--,jj++)
					if(i!=k+1)a[i][j]=(((LL)c[i+1][jj+1]*(LL)mpa[i-jj])%m*(LL)mpb[jj])%m;
					else a[i][j]=a[i-1][j];
		}
	}
	mat mult(mat b){
		mat c(0);
		for(int i=0;i<=k+1;i++)
		for(int j=0;j<=k+1;j++)
		for(int l=0;l<=k+1;l++)
				c.a[i][j]=(c.a[i][j]+((LL)a[i][l]*(LL)b.a[l][j])%m)%m;	
		return c;
	}
	int sn(){
		int res=0;
		for(int i=0;i<=k;i++){
			res=(res+(((LL)a[k+1][i]*(LL)mpf1[k-i])%m*(LL)mpf2[i])%m)%m;
		}
		res=(res+((LL)a[k+1][k+1]*(LL)s2)%m)%m;
		return res;	
	}
};
mat binMat(int x){
	if(x<=1)return mat(x);
	mat r=binMat(x/2);
	r=r.mult(r);
	if(x&1)r=r.mult(mat(1));
	return r;
}
int main(){	
	scanf("%d",&cas);
	while(cas--){
		scanf("%d%d%d%d%d%d%d",&f1,&f2,&a,&b,&k,&n,&m);
		init();
		if(n==1)printf("%d\n",s1);
		else if(n==2)printf("%d\n",s2);
		else{
			mat r=binMat(n-2);
			printf("%d\n",r.sn());
		}
		
	}
	
	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值