【题解】HDU Homework(倍增)

【题解】HDU Homework(倍增)

矩阵题一定要多多检查一下是否行列反了...

一百个递推项一定要存101个

说多了都是泪啊

一下午就做了这一道题因为实在是太菜了太久没写这种矩阵的题目...

设一个行向量\(e\),和一个增逛矩阵\(A\),他们咋定义的见我那篇讲线性递推博客

现在我们再预处理\(st\)矩阵数组,其中\(st_i=A^{2^i}\)

考虑这样一种做法,我们考虑让\(e\)总共有101个值,然后当第一个值被增逛为\(f_{q.n-100}\)时,暴力将\(e\)中的第\(101\)项修改。中间的转移直接利用这个倍增数组。中间的转移由于是倍增,而且是行乘以一个矩阵,这样的话复杂度就是\(O(m^2)\)。考虑一个询问就要处理一次最终复杂度\(O(m^3\log n+qm^2\log n)\)

细节要处理一下

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(c<48||c>57)f|=c==45,c=getchar();
      while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
const int maxn=1e2+3;
const int mod=1e9+7;
int n,m,q,T;
const int M=101;
struct MAT{
      int data[maxn][maxn];
      MAT(){memset(data,0,sizeof data);}
      inline int*operator [](int x){return data[x];}
      inline MAT operator *(MAT&f){
        MAT ret;
        for(int k=1;k<=M;++k)
          for(int t=1;t<=M;++t)
            for(int i=1;i<=M;++i)
                  ret[t][i]=(ret[t][i]+1ll*data[t][k]*f[k][i])%mod;
        return ret;
      }
}st[31];

struct line{
      int data[maxn];
      line(){memset(data,0,sizeof data);}
      inline int&operator[](int x){return data[x];}
      inline line operator *(MAT&f){
        line ret;
        for(int i=1;i<=M;++i)
          for(int t=1;t<=M;++t)
            ret[t]=(ret[t]+1ll*data[i]*f[i][t])%mod;
        return ret;
      }
}e;

int init[maxn];
pair<pair<int,int>,vector<int> > Q[maxn];

inline void pow(const int&num){
      for(int t=0;t<30;++t)
        if(num>>t&1)
          e=e*st[t];
}

int main(){
      int cnt=0;
      while(~scanf("%d%d%d",&n,&m,&q)){
        memset(st[0].data,0,sizeof st[0].data);
        memset(e.data,0,sizeof e.data);
        for(int t=1;t<=m;++t) e[t]=init[t]=qr()%mod;
        for(int t=1;t<M;++t) st[0][t+1][t]=1;
        T=qr();
        for(int t=1;t<=T;++t) st[0][M-t+1][M]=qr()%mod;
        for(int t=1;t<30;++t) st[t]=st[t-1]*st[t-1];
        for(int t=1;t<=q;++t){
          Q[t].first.first=qr();
          Q[t].first.second=qr();
          Q[t].second.clear();
          Q[t].second.push_back(0);
          for(int i=1;i<=Q[t].first.second;++i) Q[t].second.push_back(qr());
        }
        sort(Q+1,Q+q+1);
        int l=1;
        for(int t=m+1;t<=M;++t){
          if(t==Q[l].first.first&&l<=q){
            int ret=0;
            for(int i=1;i<=Q[l].first.second;++i)
                  ret=(ret+1ll*init[t-i]*Q[l].second[i])%mod;
            init[t]=ret;
            ++l;
          }
          else {
            int ret=0;
            for(int i=1;i<=T;++i)
                  ret=(ret+1ll*init[t-i]*st[0][M-i+1][M])%mod;
            init[t]=ret;
          }
        }
        for(int t=1;t<=M;++t) e[t]=init[t];
        if(n<=M) {printf("Case %d: %d\n",++cnt,init[n]); continue;}
        int cur=1;
        for(;l<=q;++l){
          if(Q[l].first.first>n) break;
          pow(Q[l].first.first-(cur+M-1));
          cur+=Q[l].first.first-(cur+M-1);
          e[M]=0;
          for(int t=1;t<=Q[l].first.second;++t)
            e[M]=(e[M]+1ll*Q[l].second[t]*e[M-t])%mod;
        }
        pow(n-cur);
        printf("Case %d: %d\n",++cnt,e[1]);
      }
      return 0;
}

转载于:https://www.cnblogs.com/winlere/p/11559912.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值