题意:
有 n n n个数字 a 1 , 2 , . . n a_{1,2,..n} a1,2,..n,你的初始能量为 a 0 a_0 a0,每次只能选取小于等于当前能量的数字,选完数字 a i a_i ai后当前能量会加上 a i a_i ai,求选完所有数字的方案数 m o d 1 e 9 + 7 mod\ 1e9+7 mod 1e9+7
n ≤ 1 e 5 , 0 ≤ a i ≤ 50 n\le1e5,0\le a_i\le 50 n≤1e5,0≤ai≤50
解题思路:
注意到 a i a_i ai很小,当前能量超过 a i a_i ai的上限之后就可以随意选取了。所以我们可以考虑直接记忆化搜索,注意把0单独拎出来。
搜 索 结 点 数 目 = 50 的 划 分 数 = 204226 搜索结点数目=50的划分数=204226 搜索结点数目=50的划分数=204226。
当前搜索的状态可以哈希之后存到unorder_map里面,也可以自己写个哈希去存,直接map结构体会超时,map哈希值也会超时。
复杂度 O ( f ( 50 ) ∗ 50 ∗ 映 射 函 数 平 均 查 找 时 间 ) O(f(50)*50*映射函数平均查找时间) O(f(50)∗50∗映射函数平均查找时间)
ac代码:
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define ull unsigned long long
using namespace std;
const int mod = 1e9 + 7;
struct node{
int a[51];
node(){
memset(a,0,sizeof a);}
};
const int maxn = 1e5 + 50;
int fac[maxn], ifac[maxn];
int qm(int a, int b){
int res = 1;
while(b){
if(b&1) res = (ll)res*a%mod;
a = (ll)a*a%mod;
b >>= 1;
}return res;
}
int n;
unordered_map<ull,int> mp;
ull sed = 131;
ull p[55];
node state;
int dp(int cur, int res){
if(res == 0) return 1;
if(cur >= 50){
return fac[res];
}
ull ha = 0;
for(int i = 50; i > 0; --i) ha = ha*sed+state.a[i];
if(mp.find(ha) != mp.end()) return mp[ha];
int ans = 0;
for(int i = 1; i <= cur; ++i){
if(!state.a[i]) continue;
int t = state.a[i];
--state.a[i];
ans = (ans+(ll)t*dp(cur+i, res-1)%mod)%mod;
++state.a[i];
}
mp[ha] = ans;
return ans;
}
int main()
{
p[0] = 1; for(int i = 1; i < 55; ++i) p[i] = p[i-1]*sed;
fac[0] = ifac[0] = 1;
for(int i = 1; i < maxn; ++i) fac[i] = (ll)fac[i-1]*i%mod;
ifac[maxn-1] = qm(fac[maxn-1], mod-2);
for(int i = maxn-2; i > 0; --i) ifac[i] = (ll)ifac[i+1]*(i+1)%mod;
scanf("%d", &n);
int cur, num = 0, z = 0; scanf("%d", &cur);
for(int i = 1; i <= n; ++i){
int x; scanf("%d", &x);
if(x == 0) z++;
else state.a[x]++, num++;
}
int ans = dp(cur, num);
ans = (ll)ans * (ll)fac[n]%mod*(ll)ifac[n-z]%mod;
cout<<ans<<endl;
}