矩陣快速冪(加法+取min)

早上想寫一些相關的證明,發現自己出了結合律和一些感性認識外,講不出理性的證明。。。

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

cf352E

思路:看完題( )之後,可以得到一個推論:放完i個括號之後,最多不會超過2n個左括號未匹配。
那麼,先預處理出從i個未匹配的左括號放n個括號之後轉移到j個未匹配的左括號需要的 最小花費,然後直接做冪次爲m的矩陣快速冪即可。

#include <bits/stdc++.h>
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 <bits/stdc++.h>
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章