哈哈哈,失踪人员回归。最近总是在忙一些乱七八糟的事情,终于有了一段空闲时间可以刷刷题了。学姐给我准备了kuangbin先生的矩阵专题,作为我的矩阵终结专题。(初学者请先看我的矩阵专题前三篇)
第一题 CodeForces 450B
分析:这道题看似是一道矩阵题,但是实际上是一道模拟题。看题目给出的公式 f(i)=f(i-1)+f(i+1),i>=2,那么转换一下得到了f(i)=f(i-1)-f(i-2),i>=3,当然这题可以使用矩阵快速幂来算,但是多此一举。不妨将f(1)=x f(2)=y代进去计算前几项,那么依次得到 x y y-x -x -y x-y x y ......,我们发现这个数列是有循环节的,而且必定为6,那么只需要计算前六个,n取一下模即可得到答案。注意:x y可能是负数,但是答案一定得是非负整数。
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define maxn 200000+10
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define mp make_pair
#define FI first
#define SE second
#define IT iterator
#define PB push_back
#define Times 10
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double eps = 1e-10;
const double pi = acos(-1.0);
const ll mod = 1e9+7;
const int inf = 0x3f3f3f3f;
inline void RI(int& x)
{
x=0;
char c=getchar();
while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
bool flag=1;
if(c=='-')
{
flag=0;
c=getchar();
}
while(c<='9'&&c>='0')
{
x=x*10+c-'0';
c=getchar();
}
if(!flag)x=-x;
}
//--------------------------------------------------
ll dat[10];
int main(){
//freopen("d:\\acm\\in.in","r",stdin);
ll n;
scanf("%I64d %I64d",&dat[1],&dat[2]);
dat[1]=(dat[1]+mod)%mod;
dat[2]=(dat[2]+mod)%mod;
scanf("%I64d",&n);
dat[0]=(dat[1]-dat[2]+mod)%mod;
for(int i=3;i<6;i++)
dat[i]=(dat[i-1]-dat[i-2]+mod)%mod;
printf("%I64d\n",dat[n%6]);
return 0;
}
题目大意:给你一个矩阵,矩阵第一行中,第一个数为空,之后的数就是233,2333,23333。。。一直轮下去。然后,矩阵第一列上,第一个数为空(即第一行第一列的那个数为空),之后的所有数会给出。接着,告诉你这个矩阵其他元素都是按照maze[i][j]=maze[i-1][j]+maze[i][j-1]的规律来计算的,让你求出矩阵右下角的数是多少。
分析:一开始没有往矩阵方面想,前段时间数论做多了之后很容易就发现最后的maze[n][m]的数可以通过已知的数乘一个数(满足一个组合数,杨辉三角),然后相加得到。虽然我公式推出来了,但是复杂度太高,而且写起来还麻烦,就被我pass了,有兴趣的朋友可以自己推一推写一写。然后回过去想矩阵做法的话,立刻就想到了(提示n的值很大,但是m的值不大)。将输入的23(第一个23是补充上去的,为了和之后的23333对齐),maze[1][0],maze[2][0],maze[3][0],......作为初始值,根据公式很容易看出转换的矩阵,除了第一个233是前一个23乘10加3,其他的都是maze[i][j]=maze[i-1][j]+maze[i][j-1]。
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define maxn 10+10
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define mp make_pair
#define FI first
#define SE second
#define IT iterator
#define PB push_back
#define Times 10
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double eps = 1e-10;
const double pi = acos(-1.0);
const ll mod = 1e9+7;
const int inf = 0x3f3f3f3f;
inline void RI(int& x)
{
x=0;
char c=getchar();
while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
bool flag=1;
if(c=='-')
{
flag=0;
c=getchar();
}
while(c<='9'&&c>='0')
{
x=x*10+c-'0';
c=getchar();
}
if(!flag)x=-x;
}
//--------------------------------------------------
ll mul(ll a,ll b){
ll ans=0;
while(b){
if(b&1)ans=(ans+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return ans;
}
struct matrix{
int n;
ll maze[maxn][maxn];
void init(int n){
this->n=n;
clr(maze,0);
}
matrix operator * (const matrix& rhs){
matrix ans;
ans.init(n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
for(int k=0;k<n;k++)
ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%10000007;
return ans;
}
};
matrix qlow(matrix a,int n){
matrix ans;
ans.init(a.n);
for(int i=0;i<a.n;i++)ans.maze[i][i]=1;
while(n){
if(n&1)ans=ans*a;
a=a*a;
n>>=1;
}
return ans;
}
int main(){
//freopen("d:\\acm\\in.in","r",stdin);
int n,m;
while(~scanf("%d %d",&n,&m)){
matrix ans;
ans.init(n+2);
for(int i=1;i<=n;i++)
scanf("%lld",&ans.maze[0][i]);
ans.maze[0][0]=23;
ans.maze[0][n+1]=1;
matrix ant;
ant.init(n+2);
for(int i=0;i<=n;i++)
ant.maze[0][i]=10,ant.maze[n+1][i]=3;
ant.maze[n+1][n+1]=1;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
ant.maze[i][j]=1;
ant=qlow(ant,m);
ans=ans*ant;
printf("%lld\n",ans.maze[0][n]);
}
return 0;
}
分析:简单来看就是一个数列计算问题。F(0)=0.如果i为奇数,那么F(i)=2*F(i-1)+1;如果i为偶数,那么F(i)=2*F(i-1)。很显然是一道矩阵问题,我们不妨将两步合成一步来做F(i)=4*F(i-2)+2,i为偶数,那么我们按照2个2个来计算,计算到不超过n的最大偶数。最后,如果n为奇数的话,再计算一步即可。
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define maxn 0+10
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define mp make_pair
#define FI first
#define SE second
#define IT iterator
#define PB push_back
#define Times 10
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double eps = 1e-10;
const double pi = acos(-1.0);
const ll mod = 1e9+7;
const int inf = 0x3f3f3f3f;
inline void RI(int& x)
{
x=0;
char c=getchar();
while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
bool flag=1;
if(c=='-')
{
flag=0;
c=getchar();
}
while(c<='9'&&c>='0')
{
x=x*10+c-'0';
c=getchar();
}
if(!flag)x=-x;
}
//--------------------------------------------------
int m;
struct matrix{
int n;
ll maze[maxn][maxn];
void init(int n){
this->n=n;
clr(maze,0);
}
matrix operator * (const matrix& rhs){
matrix ans;
ans.init(n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
for(int k=0;k<n;k++)
ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%m;
return ans;
}
};
matrix qlow(matrix a,ll n){
matrix ans;
ans.init(a.n);
for(int i=0;i<a.n;i++)ans.maze[i][i]=1;
while(n){
if(n&1)ans=ans*a;
a=a*a;
n>>=1;
}
return ans;
}
int main(){
//freopen("d:\\acm\\in.in","r",stdin);
int n;
while(~scanf("%d %d",&n,&m)){
matrix ant;
ant.init(2);
ant.maze[0][0]=4;
ant.maze[1][0]=2;
ant.maze[1][1]=1;
ant=qlow(ant,n/2);
ll ans=ant.maze[1][0];
if(n&1)ans=(ans*2+1)%m;
cout<<ans<<endl;
}
return 0;
}
分析:不会做,看了别人的题解才会的。这里我讲一下思路,作为纯DP题的话,转移方程很好想,但是score太大,无法记忆化搜索;但是作为纯矩阵题的话,一开始完全想不到如何构造矩阵。然后把两者合在一起想,就发现还是矩阵的套路,但是要加上DP的思想(以DP的状态为转移)。将一开始所有可能转移的数据全部算出来作为初始数据,部分数据直接移位就行了,主要就是最后一行的数据(要计算的新数据),需要用到前面所有数据来计算。
我知道读者肯定没有看懂,无妨,请转到这位大神的博客,他写得很好,这里我贴下我的代码
http://www.cnblogs.com/AOQNRMGYXLMV/p/5256508.html
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>