题意:求满足题目的三个要求的置换的方案
思路:
分析题意发现是一个多重背包
设每个物品的代价为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;
}