zoukankan      html  css  js  c++  java
  • 年后排序+ST表+ksm+矩阵ksm+斐波那契数列矩阵ksm+归并排序

    看点

    • 牛逼的斐波那契切矩阵快速幂

    排序

    STL针对区间的函数

    1. (sort)
    2. (merge_sort),归并
    3. (quick_sort),快速
    4. (heap_sort),堆排序

    其他

    1. (reverse (a+1,a+n+1)) 翻转 (a+1)(a+n)
    2. (unique (a+1,a+n+1)) 去重数组,例子:(11233556667-->1234567...),前提条件,去重数组必须是有序
    //unique有返回值的
    //int m=unique(z+1,z+n+1)-z-1,去重得到的数量
    z[1]--z[n];
    sort(z+1, z+1+n);
    int m=unique(z+1,z+1+n)-z-1;
    

    计数排序(桶排序)

    (n<10^6, a[i]<10^6)

    (cnt[])

    线性算法(O(N+M))

    int main()
    {
    	scanf("%d", &n);
    	for (int i=1;i<=n;i++)
    	{
    		int v;
    		scanf("%d",&v);
    		cnt[v]++;
    	}
    	for (int a=0;a<=1000000;a++)
    		for (int b=1;b<=cnt[a];b++)
    			printf("%d
    ",a);
    }
    //条件范围只可以小于1000000
    //复杂度O(M+N)
    

    归并排序(分治)

    核心思想:分

    三个目标:左排序,右排序,归并,无限循环

    :dfs

    :两个指针每次比较左右两边最小的数,通过不断的比较,可以将两个有序数组组成一个有序数组

    (O(nlogn))

    1. 分成 (logN)
    2. 每一层都是 (O(N)), 所以 (O(NlogN))
    int z[23333];
    
    void merge_sort(int l,int r)//现在对z[1]--z[n]归并排序
    {
    	//分
    	if (l==r) return;
    	int mid=(l+r)>>1;
    	merge_sort(1,mid);
    	mergr_sort(mid+1,r);
        //治
        int p1=l, p2=mid+1;//p1代表左边第一个数, p2 代表右边第一个数
        for (int a=l;a<=r;a++)
        {
            if (p1>mid) y[a]=z[p2++];//左边的数都用逛了
            else if(p2>r) y[a]=z[p1++];
            else if(z[p1]<z[p2]) y[a]=z[p1++];
            else y[a]=z[p2++];
              
        }
        for (int a=l;a<=r;a++) z[a]=y[a];
    }
    //O(nlogn)
    

    逆序对

    条件: (i<j,a[i]>a[j])

    思路:

    • 分:存在三种情况左右中,算出三种情况的数量并合起来
    • 治:排序的过程,计算中的情况
    • 如果治时在去右边去数时,加上最右边x就可以,妙蛙~
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #define int long long 
    
    using namespace std;
    
    const int B=1e6+10;
    
    int z[B],n,y[B];
    int ans;
    
    int merge_sort(int l,int r)
    {
      if (l==r) return 0;
      int mid=(l+r)>>1;
      int  ans=merge_sort(l,mid)+merge_sort(mid+1,r);
      
      int p1=l,p2=mid+1;
      for (int i=l;i<=r;i++)
      {
        if(p1>mid) y[i]=z[p2++];
        else if(p2>r) y[i]=z[p1++];
        else if(z[p1]<=z[p2]) y[i]=z[p1++];
        else y[i]=z[p2++],ans+=mid-p1+1;
      }
      for (int i=l;i<=r;i++) z[i]=y[i];
      return ans;
    }   
    
     main()
    {
      scanf("%lld",&n);
      for (int i=1;i<=n;i++)  scanf("%lld",&z[i]);
      int ans=merge_sort(1,n);
      printf("%lld",ans); 
    }
    

    前缀和思想

    Q:"%@%&*&@#^@@!#&&&%&((%&%"

    变式 1:

    (a_1 imes a_2 imes a_3 imes a_4..a_n)

    (a_l... imes a_r)

    (frac{sum[r]}{sum[l]})

    变式 2:

    是否可以用前缀和维护前缀最大值?

    (b_i=max{a_1,a_2})

    无法得到区间最大值

    这叫做不满足区间减法性质

    那么改如何求

    ST表--> (动态规划)

    (f[i][j])(a_i) 开始的 (2^j) 个数的最大值

    目标

    1. (f[i][j])

      初始化 (f[i][0]=a_i)

      (f[i][j] = max{f[i][j-1],f[i+2^{j-1}][j-1]})

      思想:分治

    int f[10010][20];
    
    int main()
    {
    	scanf("%d",&n)
    	for (int i=1;i<=n;i++) cin>>a[i];
    	//先枚举j在枚举i,先求j-1,在知道j,
    	for (int i=1;i<=n;i++) f[i][0]=a[i];
    	// x << y = x*2^y
    	// x >> y = x/2^y
    	for (int j=1;(1<<j)<=n;j++)//(1<<j)=2^j
    		for (int i=1; i + (1<<j) - 1 <=n;i++)//i+(1<<j)-1 右端点
    			f[i][j] = max(f[i][j-1],f[i+(1<<(j-1)][j-1])
    }
    
    1. (f[i][j]) 求最大值

    求(2--5)的最大值 (f[2][2])

    求(2--6)的最大值

    在求(max) 中重复数字出现是不影响答案

    那么问题二就是,一个长度为 (5) 的区间可以盖住两个 (4) 区间即 (max{f[2][2],f[3][2]})

    那么覆盖区间怎么找

    #include <iostream>
    #include <cmath>
    #include <cstdio>
    
    using namespace std;
    
    const int B=1e5+10;
    
    int f[B][20], k[B], n, a[B], m;
    
    int main()
    {
      scanf("%d%d",&n,&m);
      for (int i=1;i<=n;i++) scanf("%d",&a[i]);
      for (int i=1;i<=n;i++) f[i][0]=a[i];
      for (int j=1;j<=21;j++)
        for (int i=1;i+(1<<j)-1<=n;i++)
          f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
      for (int i=1;i<=m;i++)
      {
          int l,r;
          scanf("%d%d",&l,&r);
          int kk=log2(r-l+1);
          printf("%d
    ",max(f[l][kk],f[r-(1<<kk)+1][kk])); 
      }    
    }
    

    快速幂

    (x^y\%p)

    (x<10^9)

    举个栗子

    (x^{37}= x^{18} imes x=x^{2 imes 9} imes x=x^{2 imes4 imes2} imes x imes x)

    int ksm(int x, int p, int k){
      int res = 1;
      while(p)
      {
        if(p&1) res=x*res%k;
        x*=x;//不能同时乘和取模 
        x%=k;
        p>>=1;
      }
      return res%k;
    }
    

    矩阵乘法

    矩阵大小相同才可进行运算法则

    矩阵 (A(n imes m))(B(m imes k)) 相乘,要求第一个矩阵的列数必须要等于第二个矩阵的行数,得到矩阵(C(n imes k))

    法则

    1. 具备结合律 即 ((A imes B) imes C= A imes(B imes C))
    2. 不具备交换律 即 (A imes B != B imes A)(因为横和高不同,形成的矩阵也就不同)
    3. 左分配律 即 (A imes(B+C)=A imes B+A imes C)
    4. 右分配律 即 ((A+B) imes C=A imes C + B imes C)

    矩阵的零次幂

    任何矩阵的 (0) 次幂又称单位矩阵 (E), 其定义是他的左上角到右下角的对角线(又称主对角线)为 (1), 其余全部为零 (0)

    法则是:任何矩阵和单位矩阵 (E) 相乘都得本身,如图所示

    [egin{bmatrix}1&cdots&0\vdots&1&vdots\0&cdots&1end{bmatrix} ]

    模拟过程

    [egin{bmatrix}1&2\3&4end{bmatrix} imesegin{bmatrix}1&2&3\4&5&6end{bmatrix} ]

    若得到矩阵中((2,3)) 的数字则:

    [H ofA 2_{nd} --3,4\ imes, imes\ L ofB 3_{rd} --3,3\ 9+12=21 ]

    ((2,3)) 数字为 (21)

    struct matrix
    {
      int n,m;
      int z[10][10];
      matrix(){
        n=m=0;
        memset(z,0,sizeof(z));
      }
    };
    
    matrix  operator *(const matrix &m1, const matrix &m2)
    {
      matrix m3;
      m3.n = m1.n;
      m3.m = m2.m;
      for (int i=1;i<=m3.n;i++)
        for (int j=1;j<=m3.m;j++)
          for (int k=1;k<=m1.m; k++)
            m3.z[i][j] += m1.z[i][k]*m2.z[k][j];
       return m3; 
    }
    
    int main(){
      matrix m1;
      m1.n=1,m1.m =2;
      m1.z[1][1]=1;m1.z[1][2]=2;
      
      matrix m2;
      m2.n=2; m2.m =2;
      m2.z[1][1]=1;m2.z[1][2]=1;
      m2.z[2][1]=1;m2.z[2][2]=0;
      matrix m3=m1*m2;
      for (int i=1;i<=m3.n;i++)
      {
        for (int j=1;j<=m3.m;j++)
          cout<<m3.z[i][j]<<" ";
        puts("");
      }
      return 0;
    }
    

    矩阵快速幂

    int n, k; 
    struct matrix
    {
      int z[101][101];
    };
    matrix operator *(const matrix &m1, const matrix &m2)
    {
      matrix m3;
      for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
          m3.z[i][j]=0;
      for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
          for (int k=1;k<=n;k++)
          {
            m3.z[i][j]+=m1.z[i][k]*m2.z[k][j]%mod;
            m3.z[i][j]%=mod;
          }
      return m3;
    }
    matrix ksm(matrix x,int p)
    {
      matrix res;
     for (int i=1;i<=n;i++) res.z[i][i]=1;
      while(p>0){
        if(p&1) res=res*x;
        x=x*x;
        p>>=1;
      }
      return res;
    }
    
    main() {
      cin>>n>>k;
      matrix m1;
      for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++) 
        {
          cin>>m1.z[i][j];
        }
      matrix m=ksm(m1,k);
      for (int i=1;i<=n;i++){
          for (int j=1;j<=n;j++)
            cout<<m.z[i][j]<<" ";
        puts("");
      }
      return 0;
    }
    
    
    

    斐波那契矩阵快速幂(升级版)

    首先斐波那契数列的通式是:

    [f[i] = f[i-1]+f[i-2] ]

    我们要求出 (f[n]) 就需要枚举,时间复杂度为 (O(n))

    若用矩阵乘法和快速幂,就可达到 (O(logn))

    原理:(f[i]) 受到前两项的影响,那么可以设置一个 (1 imes2) 的矩阵,原因是若所求受到的影响为 (n) 个变量,则可以设置一个 (1 imes n)的矩阵转移,即:

    [egin{vmatrix}f[i]&f[i-1]end{vmatrix} ]

    其次,利用矩阵乘法就要保证 (f[i]) 运算后是往后移一位的,与之相乘的一定是一个(2 imes 2) 的矩阵,怎么找呢?

    我们不妨这么设

    [egin{vmatrix}a&b\c&dend{vmatrix} ]

    因为两者相乘后我们得到的答案为

    [egin{vmatrix}f[i+1]&f[i]end{vmatrix} ]

    所以可以列出方程式

    [egin{cases}af[i]+cf[i-1]=f[i+1]\bf[i]+df[i-1]=f[i]end{cases} ]

    我们可以有合并同类项可得二式的解为 (b=1,d=0)

    而一式可以将右侧的 (f[i+1]) 拆分成 (f[i]+f[i-1]) 再进行合并同类项

    得:(a=1,c=1)

    综上所述,我们的相乘矩阵为:

    [egin{vmatrix}1&1\1&0end{vmatrix} ]

    那么式子就可以推导为

    [egin{vmatrix}f[i]&f[i-1]end{vmatrix} imes egin{matrix}underbrace{egin{vmatrix}1&1\1&0end{vmatrix} imesegin{vmatrix}1&1\1&0end{vmatrix}..... imesegin{vmatrix}1&1\1&0end{vmatrix}}\nend{matrix}=egin{vmatrix}f[n+1]&f[n]end{vmatrix} ]

    化简得

    [egin{vmatrix}f[i]&f[i-1]end{vmatrix} imes egin{vmatrix}1&1\1&0end{vmatrix}^{n}=egin{vmatrix}f[n+1]&f[n]end{vmatrix} ]

    因此我们可以用矩阵快速幂,在 (O(logn)) 级别求出

    太妙了~

    struct matrix
    {
    	int n,m;
    	int z[10][10];
    	matrix(){
    		n=m=0;
    		memset(z,0,sizeof(z));
    	}
    };
    matrix operator*(const matrix &m1, const matrix &m2)
    {
    	matrix m3;
    	m3.n = m1.n;
    	m3.m = m2.m;//矩阵原理
    	for (int i=1;i<=m3.n;i++)
    		//乘法分配律,乘法交换律,思考?????????????????????????
    			for (int k=1;k<=m1.m;k++)
                    	for (int j=1;j<=m3.m;j++)
    				m3.z[i][j] += m1.z[i][k] * m2.z[k][j];
    	return m3;//矩阵乘法
    }
    matrix ksm(matrix m, int n)
    {
    	if (n==0){//当n==0时存在一个特殊的矩阵上述会给出
    		matrix z;
    		z.n=z.m=m.n;
    		for (int i=1;i<=z.n;i++)
    			z.z[i][i]=1;//特殊的快速幂即只有对角线为1,其余全是0
    		return z;
    	}
    	matrix z=ksm(m,n/2);
    	z=z*z;
    	if (n%2==1) z=z*m;//利用承载运算符,所以乘的时候直接就是矩阵乘法
    	return z;
    }
    
    
    int main()
    {
    	scanf("%d",&n);
    	matrix m1;
    	m1.n =1;m1.m=2;
    	m1.z[1][1]=1;m1.z[1][2]=0;
    	
    	matrix m2;
    	m2.n=m2.m=2;
    	m2.z[1][1]=1; m2.z[1][2]=1;
    	m2.z[2][1]=1; m2.z[2][2]=0;
    	m1 = m1 * ksm(m2, n);
    	printf("%d
    "m1.z[1][2]);
    }
    
  • 相关阅读:
    释放下一代网络应用的能量[转载]
    帮助创建未来的 .NET 客户端开发
    ASP.NET Ajax替代品AjaxWidgets
    Microsoft Surface
    有意思的《致招商银行的公开信》行动!
    Applying DomainDriven Design and Patterns(ADDDP) With examples in C# and .NET
    Silverlight ASP.NET control
    StructureMap 轻量IOC框架
    DDay.iCal an iCalendar class library
    Mono ASP.NET 上几个性能调优技巧
  • 原文地址:https://www.cnblogs.com/lToZvTe/p/14407671.html
Copyright © 2011-2022 走看看