矩阵快速幂(加法+取min)

早上想写一些相关的证明,发现自己出了结合律和一些感性认识外,讲不出理性的证明。。。

所以只能先给出例题,大家感性的理解一下。。

cf352E

思路:看完题( )之后,可以得到一个推论:放完i个括号之后,最多不会超过2n个左括号未匹配。
那么,先预处理出从i个未匹配的左括号放n个括号之后转移到j个未匹配的左括号需要的 最小花费,然后直接做幂次为m的矩阵快速幂即可。

#include 
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
int n,m,a[N],b[N];
int dp[22][44];
struct uzi{
  LL A[44][44];
  uzi(){
      memset(A,0x3f3f3f,sizeof A);
  };
}G;
uzi operator * (const uzi & a,const uzi & b){
  uzi c;
  for(int i=0;i<=40;i++){
    for(int j=0;j<=40;j++){
      for(int k=0;k<=40;k++){
        c.A[i][j]=min(a.A[i][k]+b.A[k][j],c.A[i][j]);
      }
    }
  }
  return c;
}
uzi pm(){
  uzi c;
  for(int i=0;i<=40;i++)c.A[i][i]=0;
  while(m){
    if(m&1)c=c*G;
    G=G*G;
    m>>=1;
  }
  return c;
}
int main() {
  ios::sync_with_stdio(false);
  cin>>n>>m;
  for(int i=0;i<n;i++){
    cin>>a[i];
  }
  for(int i=0;i<n;i++){
    cin>>b[i];
  }
  for(int i=0;i<=40;i++){
    for(int j=0;j<=n;j++){
      for(int k=0;k<=40;k++){
        dp[j][k]=1e9;
        if(!j){
          if(k==i)dp[j][k]=0;
        }else{
          if(k){
            dp[j][k]=min(dp[j][k],dp[j-1][k-1]+a[j-1]);
          }
          if(k+1<=40){
            dp[j][k]=min(dp[j][k],dp[j-1][k+1]+b[j-1]);
          }
        }
      }
      for(int k=0;k<=40;k++){//计算左括号的转移量 从i转移到k最小花费
        G.A[i][k]=dp[n][k];
      }
    }
  }
  cout<<pm().A[0][0];
  return 0;
}

Namomo Test Round 1 C.polygon

思路:从当前六边形到下一个六边形显然只能通过1,2号节点,那么总共就有2个位置,每个位置2个方向,共四种状态。那么先手推出四种状态互相转移的最小花费。然后剩下的就跟上面的题一样了。还有一些别的细节自己需要注意一下即可。

#include 
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
int t;
LL x,y,k,l;
int dx[6][4]={//起点到4个状态
    {0,0,1,1},
    {1,1,0,0},
    {1,1,0,1},
    {0,1,1,1},
    {1,1,1,0},
    {1,0,1,1}
};
int dy[4][6]={//重点到4个状态
    {1,1,1,1,0,0},
    {1,0,1,1,0,1},
    {0,1,1,0,1,1},
    {1,1,0,0,1,1}
};
int A[4][4]={
    {2,1,2,1},
    {1,2,1,0},
    {0,1,2,1},
    {1,2,1,2}
};
struct uzi{
  LL a[4][4];
  uzi(){
    for(int i=0;i<4;i++){
      for(int j=0;j<4;j++){
        a[i][j]=1e18;
      }
    }
  }

};
uzi operator * (const uzi a,const uzi b){
  uzi c;
  for(int i=0;i<4;i++){
    for(int j=0;j<4;j++){
      for(int k=0;k<4;k++){
        c.a[i][j]=min(c.a[i][j],a.a[k][j]+b.a[i][k]);
      }
    }
  }
  return c;
}
uzi pm(LL pw){
  uzi ans,res;
  for(int i=0;i<4;i++)ans.a[i][i]=0;
  for(int i=0;i<4;i++){
    for(int j=0;j<4;j++){
      res.a[i][j]=A[i][j];
    }
  }
  while(pw){
    if(pw&1)ans=ans*res;
    res=res*res;
    pw>>=1;
  }
  return ans;
}
int main() {
  ios::sync_with_stdio(false);
  for(cin>>t;t;t--){
    cin>>x>>y>>k>>l;
    if(x>k){
      swap(x,k);
      swap(y,l);
    }
    if(x==k){
      int z=(y-l+6)%6;
      if(z==2||z==4)cout<<1<<'\n';
      else cout<<0<<'\n';
    }else{
      uzi ge=pm(k-x-1);
      LL ans=1e18;
      if(k-x-1==0){
        if((y==2||y==1) && (l==5||l==4)){
          cout<<0<<'\n';
          continue;
        }
        for(int i=0;i<4;i++){
          ans=min(ans,0ll+dx[y-1][i]+dy[i][l-1]);
        }
        cout<<ans<<'\n';
        continue;
      }
      for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
          ans=min(ans,dx[y-1][i]+ge.a[i][j]+dy[j][l-1]);
        }
      }
      cout<<ans<<'\n';
    }
  }
  return 0;
}

你可能感兴趣的:(矩阵快速幂)