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());
}
}
}