首页 > 代码库 > HDU4887_Endless Punishment_BSGS+矩阵快速幂+哈希表
HDU4887_Endless Punishment_BSGS+矩阵快速幂+哈希表
2014多校第一题,当时几百个人交没人过,我也暴力交了几发,果然不行。
比完了去学习了BSGS才懂!
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4887
Endless PunishmentTime Limit: 30000/15000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 52 Accepted Submission(s): 24Problem Description In the ancient Greek tale, Sisyphus was a king of Ephyra, he was punished for chronic deceitfulness by being compelled to roll an immense stone up a hill every day, only to watch it roll back down, and to repeat this action forever. Zeus, the God of the universe, shows some mercy for Sisyphus, so he decides to change the way of punishment. He puts N stones with the color of white or black in a line on the hill, everyday when Sisyphus rolls a new stone up the hill, the new stone is added to the back of the old N stones, and the first stone rolls down to the foot of the hill. Then Zeus shows his magic to change the color of the new N stones. Firstly, he looks at a subset S1 of the original N stones, which always includes the first stone, if an odd number of the stones are black, then the newly N-th stone will be black and white otherwise. After the first step is done, he flips the color of another subset S2 of the new N stones, black stone will become white, and white stone will become black vice versa. The following example illustrates how Zeus‘s magic works. Consider the case of N = 4, S1 = {1,3}, S2 = {2,4}. Suppose the current stone color state is ●○○○, (○ for white stone, and ● for black stone), the 1st and 3rd stone is black and white respectively, so the new 4th stone will be black, produces ○○○● after the first step. At the second step, the 2nd and 4th stone flip its color, produces ○●○○ in the end. Zeus tells to Sisyphus that, if one day after the two steps are done, the color of the stones turns to one specific state, he will get his freedom. Now given the current and final stone colors, please compute how many days are needed for Sisyphus‘s freedom? Input There are several test cases, please process till EOF. For each test case, the first line contains three integers, the first integer is N(2 ≤ N ≤ 31), the number of stones, followed by two integers S1 and S2, (1 ≤ S1,S2 ≤ N), denoting the size of two subsets as mentioned above. The second and third line contains S1 and S2 integers a and b in the increasing order, describing the two subsets. It is guaranteed that a1 always equals to 1. The last two lines each contains N integers with 0 (for white), or 1 (for black) denoting the current and final state. The first integer describes the first stone, and the last integer describes the last stone. Please note that there are about 500 test cases, fortunately, only 20 of them are big test cases. Output For each test case, output a single integer, the minimum days for Sisyphus to get his freedom. If Zeus is an absolute liar, then just simply print the sentence poor sisyphus. Sample Input 4 2 21 32 41 0 0 00 1 0 04 2 21 32 41 1 1 10 0 0 04 2 21 32 41 1 1 10 1 0 010 5 61 2 4 5 62 4 5 8 9 100 0 0 0 0 0 1 0 0 01 1 1 1 1 1 1 1 1 1 Sample Output 14poor sisyphus387 Author Fudan University Source 2014 Multi-University Training Contest 3 |
题目大意:
宙斯要一个叫西西斯斯的人搬石头,石头在山上排成一排,最多31个,按当前称为第一个、第二个、……第31个。宙斯每天把第一个石头踢下来,全部往前移一位,让西西比斯带一个新的放在第31个的位置。石头都有两种颜色黑和白,初始颜色序列和最终颜色序列给定了。
宙斯还给了2个集合,A和B,存的是若干个位置。每次宙斯要踢石头下去之前,先算一下集合A包含的那些石头有多少个黑的,要是有奇数个黑的,就把新搬上来的时候刷成黑色,否则是白色。集合A必包含第一个位置。
然后踢下去搬上来结束后,宙斯把集合B的石头都刷成反色。
每天就这2个操作,给了石头数N,集合A、集合B,起始颜色序列,最终颜色序列,求多少天能到最终颜色序列。
题解:
BSGS+矩阵快速幂+哈希表。
用一维bool向量代表一组石头排列,加一位后就能用矩阵乘法来进行那两个操作,因为xor操作和模2的矩阵乘法一模一样。石头位移很容易在矩阵乘法中表示,石头反色就是+1(或者说xor 1),这个反色可以在新的一位中实现(石头序列向量中新的一位总为1,操作矩阵中新的一列需要反色的位置为1,其他为0)。然后再弄一个矩阵来表示新石头的颜色计算操作,就是矩阵中和计算新石头的那一位有关的那一维中,集合A包含的位置为1,其他为0。(注意A包含的是踢前的位置,B包含的是踢后的位置,在生成矩阵时要注意)。然后这两个矩阵乘起来就能得到一天的操作的矩阵。
因为A集合必包含第一个位置,所以被踢下去的时候和新上来的石头的颜色是有关联的,知道一个能算另一个,所以每天的操作能逆推。我们可以弄一个正向操作的矩阵和一个逆向操作的矩阵。(直接根据题意生成,不要乱求什么逆矩阵啊)
上面是基础,下面才是关键,我们要用BSGS来大幅减少运算!
BSGS全称BabySteps GiantSteps,在我的这一篇里面有提到:http://www.cnblogs.com/yuiffy/p/3877381.html
思想是先算m=ceil(sqrt(N))(N为所有可能状态数,在这题里N=1<<n)
然后计算从起始状态开始的前m个状态,(now,i)存入蛤习表里(now为状态,因为状态最多只有1<<31个,可以用数字表示,i为状态序号)。这题最大N=1<<31,sqrt一下只有四万多,非常好存。
上面那个是babysteps,下面这个是giantsteps。(推荐两首歌:Baby Steps-Varsity,Giant Step-Astronauts(May‘n?椎名慶治))
把m次逆操作合到一起(在这题里就是用快速幂算出nc=逆矩阵的m次方),然后从终点状态开始,一次走m步(在这题里是乘上那个nc)。
因为开头连续的m个已经算好,我一次走m步,只要踏进我算好的那m个中,就得到了答案!时间复杂度直接开了个方,我都怕!
优化:
1.用unsigned int就行,不需要long long。
2.babysteps的时候可以直接位运算,不用矩阵乘法,快一点。
代码:
1 #include<cstdio> 2 #include<cmath> 3 #include<iostream> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #include<map> 8 #include<set> 9 using namespace std; 10 #define ll __int64 11 #define usint unsigned int 12 #define RE freopen("1.in","r",stdin) 13 const usint NONE=0xffffffff; 14 15 int n,s1,s2; 16 int a1[33],a2[33]; 17 usint xo; 18 19 class hash { 20 public: 21 hash() { 22 memset(a,0xff,sizeof(a)); 23 } 24 usint locate(usint x) { 25 usint l=x%MOD; 26 while(a[l]!=x&&a[l]!=NONE) l=l+1; 27 return l; 28 } 29 void insert(usint x,usint va) { 30 usint l=locate(x); 31 if(a[l]==NONE) { 32 a[l]=x; 33 v[l]=va; 34 } 35 } 36 usint get(usint x) { 37 usint l=locate(x); 38 return a[l]==x?v[l]:NONE; 39 } 40 void clear() { 41 memset(a,0xff,sizeof(a)); 42 } 43 private: 44 static const usint MOD=1000007; 45 usint a[MOD+100],v[MOD+100]; 46 } S; 47 48 struct vct { 49 bool a[33]; 50 vct(bool q[33]) { 51 for(int i=0; i<=n; i++) 52 a[i]=q[i]; 53 } 54 vct() {} 55 void clear() { 56 memset(a,0,sizeof(a)); 57 } 58 void show() { 59 for(int i=0; i<=n; i++) 60 printf("%d ",a[i]); 61 puts(""); 62 } 63 }; 64 struct matrix { 65 bool a[33][33]; 66 matrix(bool q[33][33]) { 67 for(int i=0; i<=n; i++) 68 for(int j=0; j<=n; j++) 69 a[i][j]=q[i][j]; 70 } 71 matrix() {} 72 void clear() { 73 memset(a,0,sizeof(a)); 74 } 75 76 friend matrix operator *(matrix A,matrix B) { 77 matrix re; 78 int i,j,k; 79 re.clear(); 80 for(i=0; i<=n; i++) 81 for(j=0; j<=n; j++) 82 for(k=0; k<=n; k++) 83 re.a[i][j]=(re.a[i][j]^(A.a[i][k]*B.a[k][j])); 84 //re.show(); 85 return re; 86 } 87 void danwei() { 88 memset(a,0,sizeof(a)); 89 for(int i=0; i<=n; i++) 90 a[i][i]=1; 91 } 92 void show() { 93 for(int i=0; i<=n; i++) { 94 for(int j=0; j<=n; j++) 95 printf("%d ",a[i][j]); 96 puts(""); 97 } 98 } 99 };100 101 102 inline usint atox(bool a[33],int n) {103 usint re=0;104 for(int i=0; i<n; i++)105 re=(re<<1)+a[n-i-1];106 return re;107 }108 109 inline int xtoa(bool a[33],int n,usint x) {110 for(int i=0; i<n; i++) {111 a[i]=x&1;112 x=x>>1;113 }114 }115 116 void check(bool a[33],int n) {117 for(int i=0; i<n; i++)118 printf("%2d",a[i]);119 puts("");120 }121 122 inline usint next(usint now) {123 bool a[33],j;124 usint re;125 xtoa(a,n,now);126 j=a[a1[0]];127 for(int i=1; i<s1; i++)128 j^=a[a1[i]];129 re=(now>>1)+(j<<(n-1));130 re^=xo;131 return re;132 }133 134 vct operator * (matrix mt,vct v) {135 vct re;136 int i,j;137 re.clear();138 for(i=0; i<=n; i++)139 for(j=0; j<=n; j++)140 re.a[i]=(re.a[i]^(mt.a[i][j]*v.a[j]));141 return re;142 }143 144 matrix qpow(matrix a,usint x) {145 matrix re,t;146 re.danwei();147 t=a;148 while(x>0) {149 if(x&1==1)re=re*t;150 x=x>>1;151 t=t*t;152 }153 return re;154 }155 int main() {156 usint i,j;157 bool a[33];158 usint cnt;159 usint st,ed,now,m,t;160 matrix ni,nc,n1,n2;161 vct v;162 //RE;163 while(scanf("%d%d%d",&n,&s1,&s2)!=EOF) {164 for(i=0; i<s1; i++) {165 scanf("%d",&a1[i]);166 a1[i]--;167 }168 xo=0;169 for(i=0; i<s2; i++) {170 scanf("%d",&j);171 a2[i]=j-1;172 xo=xo|(1<<(j-1));173 }174 for(i=0; i<n; i++)175 scanf("%d",&a[i]);176 st=atox(a,n);177 for(i=0; i<n; i++)178 scanf("%d",&a[i]);179 ed=atox(a,n);180 181 ///怒求矩阵182 n1.clear();183 n2.clear();184 for(i=0; i<=n; i++)185 n2.a[i][i]=1;186 for(i=0; i<s2; i++)187 n2.a[a2[i]][n]=1;188 for(i=0; i<s1; i++)189 if(a1[i]>0) n1.a[0][a1[i]-1]=1;190 n1.a[0][n-1]=1;191 for(i=0; i<n-1; i++)192 n1.a[i+1][i]=1;193 n1.a[n][n]=1;194 ni=n1*n2;195 196 ///怒存开头m个197 now=st;198 S.clear();199 m=ceil(sqrt(((usint)1)<<n));///不先强转usint的话<<31会变0200 for(i=0; i<m; i++) {201 S.insert(now,i);202 now=next(now);/**One, two, baby steps.Three, four, baby steps.Five, six, baby steps.**/203 }204 205 nc=qpow(ni,m);///怒算逆m次操作的矩阵206 207 ///怒从结尾往前m个m个地找,怒找有没有开头m个208 now=ed;209 cnt=0;210 t=S.get(now);211 while(t==NONE) {212 if(cnt>m) break;213 xtoa(v.a,n,now);214 v.a[n]=1;215 v=nc*v;/**一歩 Giant Step , 君にとってLittle だとしても**/216 now=atox(v.a,n);217 cnt++;218 t=S.get(now);219 }220 //cout<<m<<‘,‘<<cnt<<‘,‘<<t<<endl;221 if(t==NONE) printf("poor sisyphus\n");222 else printf("%u\n",cnt*m+t);223 }224 return 0;225 }