bzoj4588 coinchange
题目链接](http://www.lydsy.com/JudgeOnline/problem.php?id=4588)
说来惭愧写了这么多题竟没写过博客,这就是我的第一篇博客了QAQ。
这题在网上搜寻无果后看了半天topcoder的官方题解,真的长。。。这里简要说说。把题想象成是从零点跳到S, 且每一步距离单调不增。那么就是用矩阵m[i][j]表示第一步<=w[i]最后一步是w[j]的方案数。
首先处理出dp[i]表示要跳w[i]的距离方案数的矩阵(转移见代码),然后发现由于跳的步伐单调不增又成倍数关系,则有些点必然会经过(即w[n],w[n]*2…w[n-1],w[n-1]*2….,把S换成一个w的进制后每一位上的点)。那么就能用矩阵转移啦!!!
然后并不会O(n^3*T)做法,就只能卡常卡常啦!!!
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
using namespace std;
typedef long long ll;
int n;
int mod=1e9+7;
ll read(){
char c=getchar();ll w=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9'){w=(w<<3)+(w<<1)+c-'0';c=getchar();}
return w;
}
ll w[45],m;
inline int add(const int a,const int b){
return (a+b>=mod)?a+b-mod:a+b;
}
int D;
inline ll mo(const ll a){
return (a<mod)?a:a-a/mod*mod;
}
struct mat{
int a[45][45];
void init(){
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++) a[i][i]=1;
}
inline friend mat operator *(mat a,mat b){
mat c;memset(c.a,0,sizeof(c.a));
for(int i=1;i<=D;i++){
for(int j=1;j<=i;j++){
if(!a.a[i][j]) continue;
for(int p=1;p<=j&&p<=i;p++){
if(!b.a[j][p]) continue;
c.a[i][p]=add(c.a[i][p],mo(1LL*a.a[i][j]*b.a[j][p]));
}
}
}
return c;
}
}f[45];
inline mat quick_pow(mat a,const ll b){
if(b==1) return a;
if(b==2) return a*a;
mat ans;ans.init();
for(ll i=1;i<=b;i<<=1,a=a*a){
if(b&i) ans=ans*a;
if((i<<1)>b) break;
}
return ans;
}
int main(){
while(scanf("%d%lld",&n,&m)!=EOF){
for(int i=1;i<=n;i++) w[i]=read();
sort(w+1,w+n+1);
while(w[n]>m) n--;
for(int i=1;i<=n;i++){
f[1].a[i][1]=1;
for(int p=2;p<=n;p++) f[1].a[i][p]=0;
}
for(int i=2;i<=n;i++){
D=i;
if(w[i]/w[i-1]>2)f[i]=quick_pow(f[i-1],w[i]/w[i-1]);
else f[i]=f[i-1]*f[i-1];
f[i].a[i][i]=1;
for(int p=i+1;p<=n;p++)
for(int j=1;j<=i;j++) f[i].a[p][j]=f[i].a[p-1][j];
}
mat ans,temp;ans.init();
D=n;
for(int i=n;i>0;i--){
if(m>=w[i]){
if(m>=(w[i]<<1)){
D=min(i+1,n);
temp=quick_pow(f[i],m/w[i]);
for(int p=i+2;p<=n;p++)
for(int j=1;j<=i+1;j++)
temp.a[p][j]=temp.a[p-1][j];
D=n;
ans=ans*temp;
}
else ans=ans*f[i];
m%=w[i];
}
}
int sum=0;
for(int i=1;i<=n;i++) sum=add(sum,ans.a[n][i]);
printf("%d\n",sum);
}
return 0;
}
处处都在卡常(面向数据编程。。。)