Kiki & Little Kiki 2(矩阵快速幂(推理))

【题目来源】:https://vjudge.net/problem/HDU-2276
【题意】
给出编号为0-n-1的灯的状态,0代表熄灭,1代表已开,并且这些灯有序围成一个圆,也就是说0号的左边是n-1号。
那么,给定一个游戏规则,每次操作之后,如果当前灯的左边灯状态是1,那么当前灯就要改变状态,如果是1,就要变成0,如果是0,反之。
那么经过m次操作之后,输出这些灯的状态。
分析样例:
0101111
编号为0:左边是1,0变化成为1;
编号为1:左边是是0,不变。(对比的是没变之前的)
编号为2:左边是1,0变成1;
编号为3:左边是0,不变。
编号为4:左边是1,变为0;
编号为5:左边是1,变为0;
编号为6:左边是1,变为0;
那么经过一次操作之后,得出的状态是1111000。
【思路】
看到那么大的变换次数,想到快速幂解决,但是怎么构建初始矩阵呢?
现在可以这么想:(拿第一组样例来说)
某个矩阵 乘以0101111得到 1111000
因为当前灯的状态与前一台有关系,所以会发现:
如果前一项是1,后一项是1,那么会得到 0
如果前一项是1,后一项是0,那么会得到 1
如果前一项是0,后一项是1,那么会得到 1
如果前一项是0,后一项是0,那么会得到 0;
发现像极了某种运算,那就是异或(^)。
那么就可以由这个去构建矩阵:
1 0 0 0 0 0 1
1 1 0 0 0 0 0
0 1 1 0 0 0 0
0 0 1 1 0 0 0
0 0 0 1 1 0 0
0 0 0 0 1 1 0
0 0 0 0 0 1 1

0
1
0
1
1
1
1

1
1
1
1
0
0
0
(只看第一列。。。)
结果第一个1意思就是编号为0的灯的状态为已开,模拟一下:
(1*0+0*1+0*0+0*1+0*1+0*1+1*1)%2=1。
对,其他的自行模拟。(比较懒。。。)
然后呢,要想到,一般的话,矩阵乘法是有三个for的,也就是O(n³)的复杂度,但是这里的n是100, 里面还有一些操作,所以,为了节省时间。引入一个叫做:循环矩阵的概念(自行点开。。。)
循环矩阵遵循代数运算法则。对于两个循环矩阵 A 与 B 来说,A + B 也是循环矩阵。A*B 也是循环矩阵,并且 A*B=B*A
既然循环矩阵可以由上一行的数字得到下一行,那么我们只需要在矩阵乘法里算出一行,递推出其他行,那么只需要O(n²)的时间复杂度。
还有,为了节省时间,矩阵乘法里1*0其他的等等都可以用&运算来代替。
【代码】

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int mod=200907;
const double esp=1e-5;
typedef unsigned long long ll;
typedef long long LL;
int n,k;
char s[105];
struct mat
{
    int a[105][105];
} ans,base;
void print(mat t)
{
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            printf("%d ",t.a[i][j]);
        }
        printf("\n");
    }
}
mat operator*(mat t,mat tt)
{
    mat r;
    for(int i=1; i<=n; i++)
    {
        r.a[1][i]=0;
        for(int j=1; j<=n; j++)
            r.a[1][i]^=(t.a[1][j]&tt.a[j][i]);
    }
    for(int i=2; i<=n; i++)
        for(int j=1; j<=n; j++)
            r.a[i][j]=r.a[i-1][(j-2+n)%n+1];
    return r;
}
void pow_mat()
{
    mem(ans.a,0);
    for(int i=1; i<=n; i++)
        ans.a[i][i]=1;
    mem(base.a,0);
    for(int i=1; i<=n; i++)
    {
        base.a[i][i]=1;
        i==1?base.a[1][n]=1:base.a[i][i-1]=1;
    }
    while(k)
    {
        if(k&1)
            ans=ans*base;
        base=base*base;
        k>>=1;
    }
}
int main()
{
    while(~scanf("%d",&k))
    {
        scanf("%s",s);
        n=strlen(s);
        pow_mat();
        for(int i=1; i<=n; i++)
        {
            int x=0;
            for(int j=1; j<=n; j++)
                x^=(s[j-1]-'0')&ans.a[i][j];
            printf("%d",x);
        }
        printf("\n");
    }
}

你可能感兴趣的:(ACM竞赛,【数论】--矩阵快速幂,ACM的进程)