zoukankan      html  css  js  c++  java
  • HDU 2276 Kiki & Little Kiki 2( 矩阵快速幂 + 循环同构矩阵 )


    蒟蒻的我还需深入学习

    **链接:****传送门 **

    题意:给出一个长度为 n,n 不超过100的 01 串 s ,每当一个数字左侧为 1 时( 0的左侧是 n-1 ),这个数字就会发生改变,整个串改变一次需要 1s ,询问 M s 后此串变为什么样子,例如 0101111 ,1s 后变为 1111000

    思路:

    • 根据题意可以得到这样一个规律 s[ i ] = ( s[ i - 1 ] + s[ i ] ) % 2,特别的 s[ 0 ] = ( s[ n-1 ] + s[ 0 ] ) % 2 ( s[ ] 不再考虑为char ),构造矩阵a、b、ans

      • 假设 01 串长为 5,下图为矩阵a
      1 0 0 0 1
      1 1 0 0 0
      0 1 1 0 0
      0 0 1 1 0
      0 0 0 1 1
      • 矩阵b
      s0 0 0 0 0
      s1 0 0 0 0
      s2 0 0 0 0
      s3 0 0 0 0
      s4 0 0 0 0
      • 矩阵 ans
      s0 0 0 0 0
      s1 0 0 0 0
      s2 0 0 0 0
      s3 0 0 0 0
      s4 0 0 0 0
      • 矩阵 ans = a * b,假设经过 Ms 后矩阵 ans = pow( a, M ) * b,此时的 b 中的 s[ ] 为起始的 01 串,所以通过矩阵快速幂就可以解决了
    • 解决此题的三种姿势

      • 1.普通矩阵快速幂,经过上面分析很容易写出该方法对应的程序

      • 2.循环同构矩阵优化矩阵快速幂,经上面分析可以看出矩阵 a 为典型的循环同构矩阵,( 什么是循环矩阵?**戳这里! ** )对于循环同构矩阵可以先算出来第 0 行然后递推下面的剩余行,从而将计算 pow( a , n ) 的复杂度 O( n ^ 3 ) 降为 O( n ^ 2 ),需要注意的时,这种方法应该只能用来优化同构矩阵的计算,如果是一个同构矩阵 × 其他矩阵,递推剩下行数是错误的!这道题不会卡复杂度......

      • 3.循环同构矩阵优化矩阵快速幂 + 位运算,可以把矩阵乘法中的 * 换成 & , % 2 换成 ^(XOR)


    姿势1:

    /*************************************************************************
        > File Name: hdu2276.cpp
        > Author:    WArobot 
        > Blog:      http://www.cnblogs.com/WArobot/ 
        > Created Time: 2017年05月04日 星期四 14时32分45秒
     ************************************************************************/
    
    #include<bits/stdc++.h>
    using namespace std;
    
    int n,len;
    string s;
    const int MOD = 2;
    const int maxn = 110;
    #define ll long long
    #define mod(x) ((x)%MOD)
    #define cls(x) memset(x,0,sizeof(x));
    
    struct mat{
    	int m[maxn][maxn];
    }unit;
    
    void init_unit(){
    	for(int i=0;i<maxn;i++)	 unit.m[i][i] = 1;
    }
    mat operator *(mat a,mat b){
    	mat ret;
    	ll  x;
    	for(int i=0;i<len;i++){
    		for(int j=0;j<len;j++){
    			x = 0;
    			for(int k=0;k<len;k++)
    				x += mod( a.m[i][k]*b.m[k][j] );
    			ret.m[i][j] = mod(x);
    		}
    	}
    	return ret;
    }
    mat pow_mat(mat a,int x){
    	mat ret = unit;
    	while(x){
    		if(x&1)	ret = ret*a;
    		a = a*a;
    		x >>= 1;
    	}
    	return ret;
    }
    
    mat a,b;
    void init(){
    	cls(a.m);
    	a.m[0][0] = a.m[0][len-1] = 1;
    	for(int i=1;i<len;i++)	a.m[i][i-1] = a.m[i][i] = 1;
    }
    int main(){
    	init_unit();
    	while(cin >> n >> s){
    		len = s.size();
    		init();
    
    		mat ans = pow_mat(a,n);
    		cls(b.m);
    		for(int i=0;i<len;i++)
    			b.m[i][0] = s[i]-'0';
    		ans = ans*b;
    		for(int i=0;i<len;i++)	printf("%d",ans.m[i][0]);
    		printf("
    ");
    	}
    	return 0;
    }
    

    姿势2:

    /*************************************************************************
        > File Name: hdu2276t2.cpp
        > Author:    WArobot 
        > Blog:      http://www.cnblogs.com/WArobot/ 
        > Created Time: 2017年05月04日 星期四 14时57分05秒
     ************************************************************************/
    
    #include<bits/stdc++.h>
    using namespace std;
    
    int n,len;
    string s;
    const int MOD = 2;
    const int maxn = 110;
    #define ll long long
    #define mod(x) ((x)%MOD)
    #define cls(x) memset(x,0,sizeof(x));
    
    struct mat{
    	int m[maxn][maxn];
    }unit;
    
    void init_unit(){
    	for(int i=0;i<maxn;i++)	 unit.m[i][i] = 1;
    }
    // 根据循环矩阵可以优化矩阵乘法
    // 因为循环矩阵a[i][i] = a[i-1][i-1] , 所以只需要计算出第0行然后递推剩下的其他行就ok了
    mat operator *(mat a,mat b){
    	mat ret;
    	cls(ret.m);
    	for(int i=0;i<len;i++)
    		for(int j=0;j<len;j++)
    			ret.m[0][i] = mod( ret.m[0][i] + mod(a.m[0][j]&b.m[j][i]) );
    	for(int i=1;i<len;i++)
    		for(int j=0;j<len;j++)
    			ret.m[i][j] = ret.m[i-1][ (j-1+len)%len ];
    	return ret;
    }
    mat pow_mat(mat a,int x){
    	mat ret = unit;
    	while(x){
    		if(x&1)	ret = ret*a;
    		a = a*a;
    		x >>= 1;
    	}
    	return ret;
    }
    
    mat a,b;
    void init(){
    	cls(a.m);
    	a.m[0][0] = a.m[0][len-1] = 1;
    	for(int i=1;i<len;i++)	a.m[i][i-1] = a.m[i][i] = 1;
    }
    int main(){
    	init_unit();
    	while(cin >> n >> s){
    		len = s.size();
    		init();
    
    		// 使用循环矩阵只能计算矩阵a^n,因为只有矩阵a有循环矩阵的特点
    		mat ans = pow_mat(a,n);
    	
    		for(int i=0;i<len;i++){
    			ll tmp = 0;
    			for(int j=0;j<len;j++){
    				tmp = mod( tmp + mod(ans.m[i][j]*(s[j]-'0')) );
    			}
    			cout<<tmp;
    		}
    		cout<<endl;
    	}
    	return 0;
    }
    

    姿势3:

    /*************************************************************************
        > File Name: hdu2276t3.cpp
        > Author:    WArobot 
        > Blog:      http://www.cnblogs.com/WArobot/ 
        > Created Time: 2017年05月04日 星期四 15时41分45秒
     ************************************************************************/
    
    #include<bits/stdc++.h>
    using namespace std;
    
    int n,len;
    string s;
    const int MOD = 2;
    const int maxn = 110;
    #define ll long long
    #define mod(x) ((x)%MOD)
    #define cls(x) memset(x,0,sizeof(x));
    
    struct mat{
    	int m[maxn][maxn];
    }unit;
    
    void init_unit(){
    	for(int i=0;i<maxn;i++)	 unit.m[i][i] = 1;
    }
    // 根据循环矩阵可以优化矩阵乘法
    // 因为循环矩阵a[i][i] = a[i-1][i-1] , 所以只需要计算出第0行然后递推剩下的其他行就ok了
    mat operator *(mat a,mat b){
    	mat ret;
    	cls(ret.m);
    	for(int i=0;i<len;i++)
    		for(int j=0;j<len;j++)
    			ret.m[0][i] ^= (a.m[0][j] & b.m[j][i]);
    	for(int i=1;i<len;i++)
    		for(int j=0;j<len;j++)
    			ret.m[i][j] = ret.m[i-1][ (j-1+len)%len ];
    	return ret;
    }
    mat pow_mat(mat a,int x){
    	mat ret = unit;
    	while(x){
    		if(x&1)	ret = ret*a;
    		a = a*a;
    		x >>= 1;
    	}
    	return ret;
    }
    
    mat a,b;
    void init(){
    	cls(a.m);
    	a.m[0][0] = a.m[0][len-1] = 1;
    	for(int i=1;i<len;i++)	a.m[i][i-1] = a.m[i][i] = 1;
    }
    int main(){
    	init_unit();
    	while(cin >> n >> s){
    		len = s.size();
    		init();
    
    		// 使用循环矩阵只能计算矩阵a^n,因为只有矩阵a有循环矩阵的特点
    		mat ans = pow_mat(a,n);
    	
    		for(int i=0;i<len;i++){
    			ll tmp = 0;
    			for(int j=0;j<len;j++)
    				tmp ^= (ans.m[i][j] & (s[j]-'0'));
    			printf("%lld",tmp);
    		}
    		printf("
    ");
    	}
    	return 0;
    }
  • 相关阅读:
    睡前一分钟打造完美下半身 健康程序员,至尚生活!
    几种不伤身体的速效减肥秘方 健康程序员,至尚生活!
    头发一周洗几次才适宜? 健康程序员,至尚生活!
    夏日驱蚊虫蟑螂的最好办法! 健康程序员,至尚生活!
    WPF控件和布局
    《深入浅出WPF》笔记——绑定篇(二)
    WPF中的DataTemplate绑定使用的场合
    WPF第一个程序和XAML初探
    实习总结之jquery实例
    下一步要实战的东西
  • 原文地址:https://www.cnblogs.com/WArobot/p/6808015.html
Copyright © 2011-2022 走看看