http://acm.hdu.edu.cn/showproblem.php?pid=2276
Kiki & Little Kiki 2
Change the state of light i (if it's on, turn off it; if it is not on, turn on it) at t+1 second (t >= 0), if the left of light i is on !!!Given the initiation state, please find all lights’ state after M second. (2<= n <= 100, 1<= M<= 10^8)
If the ith character of T is '1', it means the light i is on, otherwise the light is off.
1 0101111 10 100000001
1111000 001000010
思路:1的前一个若是1,则变为0,若是0,则变为1.
0的前一个若是1,则变为1,若是0,则变为0.
前一位 后一位 得 后一位变化为***:
1 1 得 0;
0 1 得 1;
1 0 得 1;
0 0 得 0 ;
能看出是相加后%2;也就是 ^ 运算,
初始矩阵 * 系数矩阵 = 下一个矩阵
0 1 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 * 0 0 1 1 0 0 0 = 0 0 0 0 0 0 0
0 0 0 0 0 0 0 00 0 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 00 0 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 00 0 0 0 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0
所以初始矩阵乘以系数矩阵的K次方即可,矩阵快速幂轻松搞定。
奉上我的AC代码:(时间很长,还有31ms的代码在下面介绍)
Status | Accepted |
---|---|
Time | 327ms |
Memory | 1992kB |
Length | 1064 |
Lang | G++ |
Submitted | 2017-06-28 17:06:51 |
#include <iostream>
#include <queue>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 50000
#define LL long long
using namespace std;
const int m=200907;
int n;
char s[101];
struct node
{
int a[101][101];
} a,ans;
node operator * (node b,node c)
{
node d;
memset(d.a,0,sizeof(d.a));
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
for(int k=0; k<n; k++)
d.a[i][j]^=b.a[i][k]&c.a[k][j];//用运算符会比加法乘法更快一点,(用加法乘法和取余,耗时358ms)
return d;
}
void init()
{
memset(a.a,0,sizeof(a.a));
memset(ans.a,0,sizeof(ans.a));
scanf("%s",s);
n=strlen(s);
for(int i=0;i<n;i++)
ans.a[0][i]=s[i]-'0';
a.a[0][0]=1;
a.a[n-1][0]=1;
for(int i=1;i<n;i++)
{
a.a[i][i]=1;
a.a[i-1][i]=1;
}
}
int main()
{
int k;
while(~scanf("%d",&k))
{
init();
while(k)
{
if(k%2) ans= ans*a;
a= a*a;
k=k>>1;
}
for(int i=0;i<n;i++)
printf("%d",ans.a[0][i]);
printf("\n");
}
}
优化:想想是哪个地方浪费时间了,矩阵乘法可是n^3,n范围还是100,非常大了。
有一种办法能让n^3变成n^2的,因为系数矩阵是个循环矩阵,循环矩阵乘以循环矩阵,结果还是循环矩阵。
(因为循环矩阵a[i][i] = a[i-1][i-1] , 所以只需要计算出第0行然后递推剩下的其他行就ok了)
计算第一行是n^2复杂度。
for(int i=1;i<len;i++) for(int j=0;j<len;j++) d.a[i][j]=d.a[i-1][(j-1+len)%len];
这个也是n^2的复杂度。
相对于n^3复杂度大大降低。
所以刚开始不管字符串是啥,先把系数矩阵的K次方求出来,乘以初始矩阵(字符串初始矩阵)。
还有一个问题,系数矩阵的k次方存在哪里呢,答案是用 单位矩阵 和 他 相乘这样就能保证他们怎么乘都是循环矩阵了
for(int i=0;i<len;i++)
ans.a[i][i]=1;
这就是单位矩阵主对角线上的都是1,其他是0;任何矩阵乘以单位矩阵都不变。
如果不弄单位矩阵,估计会麻烦一点点了。
奉上我的AC代码:
Status | Accepted |
---|---|
Time | 31ms |
Memory | 1860kB |
Length | 1180 |
Lang | G++ |
Submitted | 2017-06-28 18:19:06 |
#include <iostream>
#include <queue>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 50000
#define LL long long
using namespace std;
int len;
struct node
{
int a[101][101];
}a,ans;
node operator * (node b,node c)
{
node d;
memset(d.a,0,sizeof(d.a));
for(int i=0;i<len;i++)
for(int j=0;j<len;j++)
d.a[0][i]^=b.a[0][j]&c.a[j][i];
for(int i=1;i<len;i++)
for(int j=0;j<len;j++)
d.a[i][j]=d.a[i-1][(j-1+len)%len];
return d;
}
void init()
{
memset(a.a,0,sizeof(a.a));
memset(ans.a,0,sizeof(ans.a));
for(int i=0;i<len;i++)
{
ans.a[i][i]=1;
a.a[i][i]=1;
a.a[i][i+1]=1;
}
a.a[len-1][0]=1;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
char s[101];
scanf("%s",s);
len=strlen(s);
init();
while(n)
{
if(n%2) ans=ans*a;
a=a*a;
n=n>>1;
}
for(int i=0;i<len;i++)
{
int x=0;
for(int j=0;j<len;j++)
x^=(s[j]-'0')&ans.a[j][i];
printf("%d",x);
}
printf("\n");
}
}