题目标签:分治(矩阵快速幂)
题目描述:对一个长度为n的字符串A,按照以下方式对其进行加密m次,得到另一个字符串B,现在给你n,m,B,求原串A。加密方式:给出一个1~n这n个数字的排列p[i],加密一次后的串B[i]=A[p[i]]。
思路:由于m很大,则 1. 有很短的循环节 or 2. 复杂度log n。一开始使用1,但是我没找到好的方法,至今不知道是否可解。由于个人问题,2也很让我费解,但事实确是如此,把p[i]转化成一个矩阵M,加密m次就相当于M^m,然后观察是否可以从M^m得到关于A串的信息。
我转化的过程是这样的:若p[i] = t,则M[i][t] = 1,其它位置均为0。很明显M是一个稀疏矩阵,每行、每列有且只有一个数,令M.a[i]表示第i行第几个是1(后来发现还可以表示第i个是1的是第几列,节省了部分空间),M.b[i]表示(第i个是1的是第几行(后来发现还可以表示第i列第几个是1)。这样表示矩阵让我们更难理解,但让O(n^3)的矩阵乘法降到了O(n),也是值得的。
之后只需要对矩阵进行快速幂就行啦,假设最后得到的矩阵为T,则T.a[i]是用来加密的,T.b[i] 是用来解密的。本人对这个题的理解还不是非常清晰,如果有任何疏漏的地方,欢迎各位提出宝贵意见。
code:
#include<iostream>
#include<string.h>
#include<string>
using namespace std;
class Mat
{
public:
int n,a[82],b[82];
Mat()
{
n=0;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
}
Mat operator * (Mat &M)
{
Mat ans;
ans.n=n;
for(int i=1;i<=n;i++)
{
ans.a[i]=M.a[a[i]];
ans.b[ans.a[i]]=i;
}
return ans;
}
};
Mat POW(Mat R,int m)
{
if(m==1)return R;
Mat P=POW(R,m/2);
P=P*P;
if(m%2==0)return P;
return P*R;
}
int main()
{
int n,m,t;
string str;
while(cin>>n>>m&&n)
{
Mat M;
M.n=n;
for(int i=1;i<=n;i++)
{
cin>>t;
M.a[i]=t;
M.b[t]=i;
}
cin.get();
getline(cin,str);
M=POW(M,m);
for(int i=1;i<=n;i++)cout<<str[M.b[i]-1];
cout<<endl;
}
return 0;
}