Kiki & Little Kiki 2 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 529 Accepted Submission(s): 303 Problem Description There are n lights in a circle numbered from 1 to n. The left of light 1 is light n, and the left of light k (1< k<= n) is the light k-1.At time of 0, some of them turn on, and others turn off. 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) Input The input contains one or more data sets. The first line of each data set is an integer m indicate the time, the second line will be a string T, only contains '0' and '1' , and its length n will not exceed 100. It means all lights in the circle from 1 to n.If the ith character of T is '1', it means the light i is on, otherwise the light is off. Output For each data set, output all lights' state at m seconds in one line. It only contains character '0' and '1. Sample Input 1 0101111 10 100000001 Sample Output 1111000 001000010 /* 先说说这个题的意思,正如上面的红字所示,当它左边的灯为开的时候改变自己的状态依此循环,判断左边的灯是在改变之前判断 上面的输入1 0101111 是1s后整体灯的状态, 由于这个 1<= M<= 10^8 时间的取值范围会非常大,会导致典型的超时,那样该怎么做呢?这种题是典型的矩阵求解题,为什么是矩阵,我 们给出一个解说 我们刚开始学了现形代数里的 矩阵 一直觉得那个不知道怎么用,现在我们来讨论它的一种应用 我们先来说矩阵的一个优点 它是将 所有一组操作集合在一个矩阵中,然后只需要一步操作就能得出最后操作结果(在这里我们会用矩阵的幂 乘以 初始状态得到最终状态)(在这里有人可能会 说 那矩阵的幂不是需要更多的时间(我们用二分法 时间复杂度由 n变为O(log(2)(N) 什么概念呢 比如原来是2^64即为18466744073709551616次 现在就变成了O(log(2)(2^64)即为64次(由原来的20位数变成了现在的2位数64 是不是很快呢)) 有一个递推式 A(n) = A(n-1) -- 2*A(n-2) + 3A(n-3) -- A(n-4) 并且知道A(0)=0;A(1)=1;A(2)=2;A(3)=3; 怎么求出A(n)呢 ? 为了求这个我们先来说明一个简单的问题: A(n)=A(n-1) + A(n-2);A(0)=1;A(1)=1; A(n) 1 1 A(n-1) = * A(n-1) 1 0 A(n-2) 1 1 1 1 A(n-2) = * * = .......... 1 0 1 0 A(n-3) | 1 1 |^(n-1) A(1) = | | * | 1 0 | A(0) 看懂了吧!那我们现在来求上面的问题 A( n) | 1 -2 3 -1 |^(n-3) A(3) A( n-1) | 1 0 0 0 | A(2) = | | * A( n-2) | 0 1 0 0 | A(1) A( n-3) | 0 0 1 0 | A(0) 这够详细吧。但是由于时间范围取的很大,相乘次数会太多,怎么解决呢?其实上面已经稍微提了一下,那就是强悍的二分法,怎么用呢?把每个矩阵当成一个数 这样就好了,好了还是看如 下AC过的代码吧*/ /*矩阵 加 二分*/ #include <stdio.h> #include <string.h> #define size 103 /* 例如: 10 100000001 初始表 /*构造矩阵*//*1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 输入表 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 结果所求的矩阵为 构造矩阵^n * 输入矩阵,每一个元素的值为乘积值mod 2 //这儿很重要 */ /* 为什么是对2取余呢? 我们从上面的总结出状态方程 A1 = (A1 +An)%2 A2 = (A1 +A2)%2 ....... 状态方程为 Ai = (Ai + A(i-1))&1 |1 0 0 0···0 0 1 | a1 |a1+an| |1 1 0 0··· 0 0 0| a2 |a1+a2| |0 1 1 0 ···0 0 0| a3 = |a2+a3| |0 0 1 1 ···0 0 0| a4 |a3+a4| | ·· | ai |……...| 这样构造矩阵的哦 */ int t,len; char s[size]; struct node { int Graph[size][size]; }unit,init,right; void InitData(int len) //初始化构造矩阵 { int i,j; for (i=0;i<len;i++) { for (j=0;j<len;j++) { unit.Graph[i][j] = (i==j); init.Graph[i][j] = right.Graph[i][j] = 0; } } init.Graph[0][0] = init.Graph[0][len-1] = 1; for(i=1;i<len;i++) init.Graph[i][i-1] = init.Graph[i][i] = 1; for(i=0;i<len;i++) right.Graph[i][0] = s[i]-'0'; } node Mul(node a,node b) //矩阵的乘法 { int i,j,k; node c; for (i=0;i<len;i++) { for (j=0;j<len;j++) { c.Graph[i][j] = 0; for (k=0;k<len;k++) c.Graph[i][j]+=a.Graph[i][k]*b.Graph[k][j]; c.Graph[i][j]%=2; } } return c; } node Cal() //二分法求矩阵的幂 { node p,q; p = unit,q = init; while (t) { if(t&1) //是奇数 则p叠乘上 p = Mul(p,q); t>>=1; //右移,相当于/2 q = Mul(q,q); } p = Mul(p,right); return p; } int main() { freopen("in.txt","r",stdin); int i; node res; while (scanf("%d",&t)!=EOF) { scanf("%s",s); len = strlen(s); InitData(len); res = Cal(); for(i=0;i<len;i++) printf("%d",res.Graph[i][0]); printf("\n"); } return 0; }
ACM解题报告
最新推荐文章于 2018-08-04 17:36:37 发布