标签:
嘛,都刷一遍好辣。
矩阵
考虑矩阵的乘法:
经典题目1 给定n个点,m个操作,构造O(m+n)的算法输出m个操作后各点的位置。操作有平移、缩放、翻转和旋转
然后盗图
考虑实际上这个变换对应着一个类似于线性变换的东西,我们显然是可以用矩阵来搞的。
而对于翻转,旋转和缩放都是线性变换。
然而这里冒出一个平移。。
来想一想,发现肯定是多一维常量,这样就好了。
我们考虑一个向量
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define PI M_PI
using namespace std;
int n,m;
struct Mat{double a[4][4];}p[10005];
Mat operator*(Mat w,Mat ww)
{
Mat c;
Rep(i,3)Rep(j,3)c.a[i][j] = 0;
Rep(i,3)Rep(k,3)Rep(j,3)c.a[i][j] += w.a[i][k] * ww.a[k][j];
return c;
}
int main ()
{
scanf("%d%d",&n,&m);
Rep(i,n)
scanf("%lf%lf",&p[i].a[1][1],&p[i].a[2][1]),p[i].a[3][1] = 1.0;
Mat res;
Rep(i,3)Rep(j,3)res.a[i][j] = (i == j);
Rep(i,m)
{
getchar();
char op;
scanf("%c",&op);
Mat ori;
Rep(i,3)Rep(j,3)ori.a[i][j] = (i == j);
if(op == ‘M‘)
{
double x,y;
scanf("%lf%lf",&x,&y);
ori.a[1][3] = x;ori.a[2][3] = y;
}
else if(op == ‘X‘)ori.a[2][2] = -1;
else if(op == ‘Y‘)ori.a[1][1] = -1;
else if(op == ‘S‘){double S;scanf("%lf",&S);ori.a[1][1] = ori.a[2][2] = S;}
else
{
double ang;
scanf("%lf",&ang);
ang = ang / 180.0 * PI;
ori.a[1][1] = cos(ang);
ori.a[1][2] = - sin(ang);
ori.a[2][1] = sin(ang);
ori.a[2][2] = cos(ang);
}
res = ori * res;
}
Rep(i,n)p[i] = res * p[i],printf("%.1f %.1f\n",p[i].a[1][1],p[i].a[2][1]);
return 0;
}
经典题目2 给定矩阵A,请快速计算出A^n(n个A相乘)的结果,输出的每个数都mod p。
考虑快速幂,那么实际上和乘法运算无异。(代码肯定以后要用得上)
经典题目3:
给定矩阵A,求
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
using namespace std;
int n,m,K,clu;
struct Matrix{int w[55][55];Matrix(){Rep(i,clu)Rep(j,clu)w[i][j] = (i == j);}};
Matrix A;
Matrix operator+ (Matrix a,Matrix b)
{
Rep(i,clu)Rep(j,clu)a.w[i][j] = (a.w[i][j] + b.w[i][j]) % m;
return a;
}
Matrix operator* (Matrix a,Matrix b)
{
Matrix c;
memset(c.w,0,sizeof(c.w));
Rep(i,clu)Rep(k,clu)Rep(j,clu)c.w[i][j] = (c.w[i][j] + 1ll * a.w[i][k] * b.w[k][j]) % m;
return c;
}
void print(Matrix a)
{
puts("PRINT");
Rep(i,clu){Rep(j,clu - 1)printf("%d ",a.w[i][j]);printf("%d\n",a.w[i][clu]);}
puts("END");
}
Matrix FP(Matrix a,int p)
{
Matrix c = Matrix();
while(p)
{
if(p & 1)c = c * a;
p >>= 1;
a = a * a;
}
return c;
}
Matrix solve(int r)
{
if(r == 1)return A;
int mid = r >> 1;
Matrix lm,rm;
lm = solve(mid);
rm = FP(A,mid) * lm;
return r & 1 ? lm + rm + FP(A,r) : lm + rm;
}
int main ()
{
scanf("%d%d%d",&clu,&K,&m);
Rep(i,clu)Rep(j,clu)scanf("%d",&A.w[i][j]);
Matrix ans = solve(K);
Rep(i,clu){Rep(j,clu - 1)printf("%d ",ans.w[i][j]);printf("%d\n",ans.w[i][clu]);}
return 0;
}
经典题目4:送给圣诞夜的礼品
题意:
n个物品分别标号为1-n。顺次给出m个置换,反复使用这m个置换对初始序列进行操作,问k次置换后的序列。n<= 100,m<=10, k<2^31。
考虑到我们可以用矩阵来写出变换的形式:
即如果i会变换到j那么在矩阵上就将
考虑这个矩阵左乘一个列向量
那么乘完之后会得到一个列向量,它就是变换之后的局面。
想一想为什么会得到那个变换后的列向量c:
则有
并且j只能取1,所以会发现向量c的第i个值等于向量b的第p个值。
考虑朴素做法:
设当前局面为P,则经过
我们已知会对C再经过一次操作
这样的话我们对op分组。
即把m个分为1组,然后依次左乘操作序列。
剩下k mod m个我们暴力即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
using namespace std;
const int N = 105;
struct Matrix{int cl,rw,w[N][N];Matrix(){memset(w,0,sizeof(w));rw = cl = 0;}}op[15];
Matrix I(int cl){Matrix c;c.cl = c.rw = cl;Rep(i,cl)c.w[i][i] = 1;return c;}
Matrix operator*(Matrix a,Matrix b)
{
Matrix c;
c.rw = a.rw,c.cl = b.cl;
Rep(i,a.rw)
Rep(k,a.cl)
Rep(j,b.cl)
c.w[i][j] += a.w[i][k] * b.w[k][j];
return c;
}
Matrix operator+(Matrix a,Matrix b){Rep(i,a.rw)Rep(j,a.cl)a.w[i][j] += b.w[i][j];return a;}
Matrix FP(Matrix a,int p)
{
Matrix c = I(a.cl);
while(p)
{
if(p & 1)c = c * a;
p >>= 1;
a = a * a;
}
return c;
}
int n,m,K;
Matrix ans,A;
void print(Matrix c){printf("%d %d\n",c.rw,c.cl);Rep(i,c.rw){Rep(j,c.cl)printf("%d ",c.w[i][j]);puts("");}}
int main ()
{
scanf("%d%d%d",&n,&m,&K);
ans.cl = 1;ans.rw = n;
A.cl = A.rw = n;
A = I(n);
Rep(i,n)ans.w[i][1] = i;
int p = K / m;
Rep(i,m)
{
Matrix c;
c.cl = c.rw = n;
Rep(j,n)
{
int ww;
scanf("%d",&ww); //把ww放到j上
c.w[j][ww] = 1;
}
op[i] = c;
A = c * A;
}
A = FP(A,p);
p = K % m;
Rep(i,p)A = op[i] * A;
A = A * ans;
Rep(i,n - 1)printf("%d ",A.w[i][1]);printf("%d ",A.w[n][1]);puts("");
return 0;
}
经典题目5:
经典题目5 《算法艺术与信息学竞赛》207页(2.1代数方法和模型,[例题5]细菌,版次不同可能页码有偏差)
大家自己去看看吧,书上讲得很详细。解题方法和上一题类似,都是用矩阵来表示操作,然后二分求最终状态。
这个没法写了。。下一题
经典题目6 给定n和p,求第n个Fibonacci数mod p的值,n不超过2^31
我们考虑到Fibonacci数列的递推公式:
递推的话肯定会死得很惨,但是仔细想想,我们可以认为:
我们想得到一个矩阵,使得:
[a,b] * A = [b + a,a]
然后我们肯定就能构造Fibonacci的转移矩阵了。。
我们发现对于任意的线性递推,都可以用矩阵乘法优化。
形如F(n) = qF(n - 1) + pF(n - 2) + k(k为常量);
那么我们可以画出矩阵:
这样就可以优化线性递推了。
矩阵的构造方法为:在右上角的(k - 1)*(k - 1)的小矩阵中的主对角线上填1,矩阵第k行填对应的系数,其它地方都填0。
如果是加一个常量,那么就再扩大一行和一列,在(k - 1,k)这个位置写常量的系数,在(k,k)的位置写1,剩下都写0。
经典题目7 VOJ1067
dp转移很好写,再加上上面的矩阵乘法的优化知识,我们可以很轻松的做出这道题(早就做出来了)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cmath>
#define Rep(i,n) for(int i = 1; i <= n ;i ++)
#define RD(i,l,r) for(int i = l;i <= r;i ++)
#define Repd(i,n) for(int i = n;i > 0 ;i --)
#define Rep_0(i,n) for(int i = 0;i < n;i ++)
#define RepG(i,x) for(int i = head[x]; ~ i;i = edge[i].next)
using namespace std;
typedef long long ll;
const int MOD = 7777777;
int n,m;
struct Matrix{ll f[12][12];};
Matrix operator*(Matrix a,Matrix b)
{
int sz = m;
Matrix c;memset(c.f,0,sizeof(c.f));
Rep(i,sz)
Rep(k,sz)
Rep(j,sz)
c.f[i][j] += a.f[i][k] * b.f[k][j],
c.f[i][j] %= MOD;
return c;
}
int read(){
char ch = getchar();
int x = 0;
bool flag = 0;
while(ch != ‘-‘ &&(ch < ‘0‘ || ch > ‘9‘))ch = getchar();
if(ch == ‘-‘)ch = getchar(),flag = 1;
while(ch >= ‘0‘ && ch <= ‘9‘)x = 10 * x + ch - ‘0‘,ch = getchar();
return flag ? -x : x;
}
/*
f[n] = f[n - 1] + f[n - 2] + f[n - 3] + f[n - 4] + f[n - 5];
|0 1 0 0 0 | |f[n - 1]| |f[n - 2]|
|0 0 1 0 0 | |f[n - 2]| |f[n - 3]|
|0 0 0 1 0 | * |f[n - 3]| = |f[n - 4]| //左乘
|0 0 0 0 1 | |f[n - 4]| |f[n - 5]|
|1 1 1 1 1 | |f[n - 5]| | f[ n ] |
//正确性显然.
//当然还有一种方法是倒着写:
|1 1 1 1 1 | |f[n - 1]| | f[ n ] |
|1 0 0 0 0 | |f[n - 2]| |f[n - 2]|
|0 1 0 0 0 | * |f[n - 3]| = |f[n - 3]| //左乘
|0 0 1 0 0 | |f[n - 4]| |f[n - 4]|
|0 0 0 1 0 | |f[n - 5]| |f[n - 5]|
*/
int main (){
m = read(),n = read();
Matrix a,b;
memset(a.f,0,sizeof(a.f));
memset(b.f,0,sizeof(b.f));
a.f[1][1] = 1;
RD(i,2,m)
a.f[i][i - 1] = a.f[1][i] = 1;
b.f[1][1] = 1;
while(n)
{
if(n & 1)b = a * b;
a = a * a;
n >>= 1;
}
printf("%lld\n",b.f[1][1]);
return 0;
}
经典题目8 给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值
把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。令
首先这个正确性是很对的。
然后这样的话就根本不用讲了,快速幂解决即可。
经典题目9 用1 x 2的多米诺骨牌填满M x N的矩形有多少种方案,M<=5,N<2^31,输出答案mod p的结果
经典题目10 POJ2778
AC自动机+矩阵乘法优化,这里因为自己不会AC自动机。。。
所以留坑。
标签:
原文地址:http://blog.csdn.net/zxn0803/article/details/51773119