解题报告:HDU_6176 Function Counting (离散化DP+矩阵快速幂)

4 篇文章 0 订阅
4 篇文章 0 订阅

题目链接

题意:求满足题目的三个要求的置换的方案

思路:

分析题意发现是一个多重背包

设每个物品的代价为x,价值为y

则物品的代价为满足(2*t+1)*x==k , t 为自然数

对应的价值为2^x

代价为1和2的物品的价值比较特殊,为2^(x-1)

另外代价为2的物品会带上一个(4,4)的物品(交叉取置换)

于是就可以得到一个线性递推方程,基于n和k的范围采用不同的方法计算结果

矩阵快速幂的复杂度为

离散化DP的复杂度为 (d(k)为k的因子个数)

以上复杂度都为估计的上界,实际复杂度很难达到

那么选k=100作为分界,使得复杂度约在1e7的级别


代码:

#include<bits/stdc++.h>

#define pii pair<int,int>
#define fi first
#define se second
const int mod = 1e9+7 , N = 105;
using namespace std;

long long qpow(long long x,long long y){
   long long res = 1;
   while(y){
      if(y&1) res = res * x % mod;
      y>>=1; x = x * x % mod;
   }return res;
}

inline int oper(int x){return qpow(2,x-(x<=2));}

vector<pii>tim;
map< int ,int > dp;
int n,m,Dp[N];



namespace fast_Matrix{

int nn ;
const int MAX_E = N;
long long tmp[MAX_E][MAX_E];
long long mat[MAX_E][MAX_E];
long long res[MAX_E][MAX_E];
inline void mut(long long a[MAX_E][MAX_E],long long b[MAX_E][MAX_E]){
   memset(tmp,0,sizeof(tmp));
   for(int i=0;i<nn;++i)
      for(int j=0;j<nn;++j)if(a[i][j])
         for(int k=0;k<nn;++k)
            tmp[i][k] = (tmp[i][k] + a[i][j] * b[j][k])%mod;
   for(int i=0;i<nn;++i)
      for(int j=0;j<nn;++j)
         a[i][j] = tmp[i][j];
}

inline void debug(long long a[MAX_E][MAX_E]){
    for(int i=0;i<nn;i++){
        for(int j=0;j<nn;j++){
            printf("%I64d%c",a[i][j],j!=nn-1?' ':'\n');
        }
    }printf("\n");
}

long long fast_mat(long long y)
{
   nn = m+1;
   memset(res,0,sizeof(res));
   memset(mat,0,sizeof(mat));
for(int i=0;i<tim.size();++i){
   if((mat[0][tim[i].fi-1]+=tim[i].se)>=mod)mat[0][tim[i].fi-1]-=mod;
}
for(int i=0;i<nn;i++){
    mat[i+1][i] = res[i][i] = 1;
}
    while(y){
        if(y & 1)mut(res,mat);
        mut(mat,mat);y>>=1;
    }
    long long val = 0;
    for(int i=0,j=m;i<=m;++i,--j)val = (val+1LL*Dp[j]*res[0][i])%mod;
    return val;
}

}
using namespace fast_Matrix;

long long work1(){
   memset(Dp,0,sizeof(Dp));Dp[0]=1;
   for(int i=0;i<=m;++i)if(Dp[i])
      for(int j=0;j<tim.size();++j){
         pii& t = tim[j];
         int k = i + tim[j].fi;
         if(k<=m)Dp[k] = ( Dp[k] + 1LL * Dp[i] * t.se ) % mod;
      }
   return n<=m?(long long)Dp[n]:fast_mat(n-m);
}

long long work2(){
   dp.clear();dp[0] = 1;
   for(auto it:dp){
      for(int j=0;j<tim.size();++j){
         pii& t = tim[j];
         int k = it.fi + tim[j].fi;
         if(k<=n)dp[k]=( dp[k] + 1LL * it.se * tim[j].se) % mod;
      }
   }return dp[n];
}

inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

int main()
{
   int T = read();
   while(T--){tim.clear();
      n = read(); m = read();
      int ed = sqrt(m+0.5);
      bool _2 = false;
      for(int i=1;i<=ed;++i){
         if(m%i==0){
            int t = m/i;
            if(i&1){
               if(t==2)_2=true,tim.emplace_back(4,4);
               tim.emplace_back(t,oper(t));
            }
            if((t&1)&&t!=i){
               if(i==2)_2=true,tim.emplace_back(4,4);
               tim.emplace_back(i,oper(i));
            }
         }
      }sort(tim.begin(),tim.end());
      if(_2&&m<4)m=4;
      printf("%lld\n",m<N?work1():work2());
   }
   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值