滑雪 E a s y \color{green}{Easy} Easy
Code:
#include
using namespace std;
const int N = 333;
int dp[N][N], mp[N][N];
int n, m;
int dfs(int a, int b)
{
if(dp[a][b]) return dp[a][b];
dp[a][b] = 1;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
for(int i = 0; i < 4; ++i)
{
int x = dx[i] + a, y = dy[i] + b;
if(x >= 1 && x <= n && y >= 1 && y <= m && mp[a][b] > mp[x][y])
{
dp[a][b] = max(dp[a][b], dfs(x, y) + 1);
}
}
return dp[a][b];
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
cin >> mp[i][j];
int ans = 0;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
ans = max(ans, dfs(i, j));
cout << ans << endl;
return 0;
}
问题描述:
有 N N N件物品和一个容量为 V V V的背包。第i件物品的费用是 c [ i ] c[i] c[i],价值是 v a l [ i ] val[i] val[i]。求解将哪些物品装入背包可使价值总和最大。
思路:
01背包的特点,每件物品可以选择放或者不放。所有不妨设 F ( i , j ) F(i, j) F(i,j)表示为,前 i i i个物品放进 j j j容量的最大价值,则其状态转移方程为:
f ( i , j ) = m a x ( f ( i − 1 , j ) , f ( i − 1 , j − c [ i ] ) + v a l [ i ] ) f(i, j) = max(f(i-1,j), f(i-1, j-c[i])+val[i]) f(i,j)=max(f(i−1,j),f(i−1,j−c[i])+val[i])
Code:
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
// 当前背包容量装不进第i个物品,则价值等于前i-1个物品
if(j < v[i])
f[i][j] = f[i - 1][j];
// 能装,需进行决策是否选择第i个物品
else
f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
}
cout << f[n][m] << endl;
空间优化:
定义** f ( i ) f(i) f(i)**:表示容量为 i i i时的最大价值,则该转移方程为:
f ( i ) = m a x ( f ( i ) , f ( i − c [ i ] ) + v a l [ i ] ) f(i) = max(f(i), f(i-c[i])+val[i]) f(i)=max(f(i),f(i−c[i])+val[i]) 注意此时,在枚举容积的时候应该是逆序
Code:
cin >> n >> m;
for(int i = 1; i <= n; i++) {
int v, w;
cin >> v >> w; // 边输入边处理
for(int j = m; j >= v; j--)
f[j] = max(f[j], f[j - v] + w);
}
cout << f[m] << endl;
问题描述:
有 N N N种物品和一个容量为 N N N的背包,**每种物品都有无限件可用。**第 i i i种物品的费用是 c [ i ] c[i] c[i],价值是 v a l [ i ] val[i] val[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
Solution: f ( i ) f(i) f(i) 表示容量为 i i i的最大价值,由于是无限可用,在枚举容量的是时候顺序枚举,转移方程: f ( i ) = m a x ( f ( i ) , f ( i − c [ i ] ) + v a l [ i ] ) f(i) = max(f(i), f(i-c[i])+val[i]) f(i)=max(f(i),f(i−c[i])+val[i])
Code:
cin >> n >> m;
for(int i = 1; i <= n; i++) {
int v, w;
cin >> v >> w;
for(int j = v; j <= m; j++)
f[j] = max(f[j], f[j - v] + w);
}
cout << f[m] << endl;
问题描述:
有 N N N种物品和一个容量为 V V V的背包。第 i i i种物品最多有 n [ i ] n[i] n[i]件可用,每件费用是 c [ i ] c[i] c[i],价值是 v a l [ i ] val[i] val[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
Easy: 0 < N , V ⩽ 100 0 < N, V \leqslant 100 0<N,V⩽100 0 < v , w , s ⩽ 100 0 < v, w, s \leqslant 100 0<v,w,s⩽100
Solution: 直接拆成01背包
Code:
#include
using namespace std;
int n, m;
int f[1010];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i)
{
int v, w, s; cin >> v >> w >> s;
for(int j = 1; j <= s; ++j) // 对s次都做一次01背包
for(int k = m; k >= v; --k)
f[k] = max(f[k], f[k-v]+w);
}
cout << f[m];
return 0;
}
Mid: 0 < N ⩽ 1000 0 < N \leqslant 1000 0<N⩽1000 0 < V ⩽ 2000 0 < V \leqslant 2000 0<V⩽2000 0 < v , w , s ⩽ 2000 0 < v, w, s \leqslant 2000 0<v,w,s⩽2000
Solution:将每件物品进行二进制拆分。例如:可以选12个物品,则可以拆成1,2,4,5,易知,对于0-12都可以由上述凑出来,如图:
1 | 1 | 7 | 1+2+4 |
---|---|---|---|
2 | 1+1 | 8 | 4+4 |
3 | 1+2 | 9 | 5+4 |
4 | 2+2 | 10 | 5+5 |
5 | 1+4 | 11 | 5+5+1 |
6 | 2+4 | 12 | 5+5+2 |
Code:
#include
using namespace std;
int n, m;
int f[2020];
struct Good{
int v,w;
};
int main()
{
cin >> n >> m;
vector<Good>g;
for(int i = 1; i <= n; ++i)
{
int v, w, s; cin >> v >> w >> s;
for(int k = 1; k <= s; k <<= 1)
{
s -= k;
g.push_back({k*v,k*w});
}
if(s > 0) g.push_back({s*v,s*w});
}
for(auto goods : g)
for(int j = m; j >= goods.v; --j)
f[j] = max(f[j], f[j-goods.v] + goods.w);
cout << f[m];
return 0;
}
Hard: 0 < N ⩽ 1000 0 < N \leqslant 1000 0<N⩽1000 0 < V ⩽ 20000 0 < V \leqslant 20000 0<V⩽20000 0 < v , w , s ⩽ 20000 0 < v, w, s \leqslant 20000 0<v,w,s⩽20000
Solution:参考题解
问题描述:
有 N N N 组物品和一个容量是 V V V 的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是 v i , j v_{i,j} vi,j,价值是 w i , j w_{i,j} wi,j,其中 i i i 是组号, j j j 是组内编号。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。数据范围均小于100
Solution: 每组进行01背包
Code:
#include
using namespace std;
int n, m;
int f[110], v[110][110], w[110][110], s[110];
int main()
{
cin >> n >> m;
for(int i = 1;i <= n; ++i)
{
cin >> s[i];
for(int j=1;j<=s[i]; ++j)
cin >> v[i][j] >> w[i][j];
}
for(int i = 1; i <= n; ++i)// 枚举组,然后01背包
{
for(int k = m; k >= 0; --k)//每个h对应每组最优解
{
for(int h = 1; h <= s[i]; ++h)
if(k >= v[i][h]) f[k] = max(f[k], f[k-v[i][h]] + w[i][h]);
}
}
cout << f[m];
return 0;
}
Code:
#include
using namespace std;
const int N = 1001;
int dp[N][N], n;
int main()
{
cin >> n;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i; ++j)
cin >> dp[i][j];
for(int i = n - 1; i >= 1; --i)
{
for(int j = 1; j <= i; ++j)
dp[i][j] += max(dp[i+1][j], dp[i+1][j+1]);
}
cout << dp[1][1];
return 0;
}
Code:
#include
using namespace std;
int main()
{
int n; cin >> n;
vectora(n), dp(n + 1, 0x3f3f3f3f);
int mx = dp[0];
for(int i = 0; i < n; ++i) cin >> a[i];
for(int i = 0; i < n; ++i)
// 不严格上升则使用upper_bound
*lower_bound(dp.begin(), dp.end(), a[i]) = a[i];
int ans = 0;
while (dp[ans] != mx) ans ++;
cout << ans << endl;
return 0;
}
Code:
#include
using namespace std;
const int N = 1010;
int dp[N][N];
int main()
{
int n, m; cin >> n >> m;
string a, b; cin >> a >> b;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
{
if(a[i - 1] == b[j - 1])
dp[i][j] = dp[i-1][j-1] + 1;
else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
cout << dp[n][m] << endl;
return 0;
}
Code:
#include
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
int dp[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n; cin >> n;
vectora(n), b(n);
unordered_mapmp;
for(int i = 0; i < n; ++i) cin >> a[i], mp[a[i]] = i;
for(int i = 0; i < n; ++i) cin >> b[i];
memset(dp, 0x3f, sizeof dp);
int mx = dp[n];
for(int i = 0; i < n; ++i)
*upper_bound(dp, dp+n, mp[b[i]]) = mp[b[i]];
int ans = 0;
while( dp[ans] != mx) ans ++;
cout << ans << endl;
return(0-0);
}
d p [ i ] [ j ] dp[i][j] dp[i][j]表示 a a a的前 i i i个字符变成 b b b的前 j j j个字符的组少操作数
转移方程:
d p [ i ] [ j ] dp[i][j] dp[i][j] =
a. 删除: d p [ i − 1 ] [ j ] + 1 dp[i-1][j] + 1 dp[i−1][j]+1
2. 添加:$dp[i][j-1] + 1$
3. 替换:$dp[i-1][j-1] + 1$
4. 不变:$dp[i-1][j-1]$
#include
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = 2020;
int dp[N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
string a, b; cin >> a >> b;
int n = a.size(), m = b.size();
for(int i = 1; i <= n; ++i) dp[i][0] = i;
for(int i = 1; i <= m; ++i) dp[0][i] = i;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
{
if(a[i-1] == b[j-1]) dp[i][j] = dp[i-1][j-1];
else {
dp[i][j] = min(min(dp[i-1][j] + 1, dp[i][j-1] + 1), dp[i-1][j-1] + 1);
}
}
cout << dp[n][m] << endl;
return(0-0);
}
Code:
#include
using namespace std;
const int N = 333, INF = 2e9;
int w[N], s[N], n;
int dp[N][N];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++i) cin >> w[i];
for(int i = 1; i <= n; ++i) s[i] = s[i - 1] + w[i];
for(int len = 2; len <= n; ++len)
{
for(int l = 1; len + l - 1 <= n; ++l)
{
int r = len + l - 1;
dp[l][r] = INF;
for(int k = l; k < r; ++k)
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]);
}
}
cout << dp[1][n] << endl;
return 0;
}
Code:
class Solution {
public:
int longestPalindromeSubseq(string s) {
int n = s.size();
vector<vector<int>>dp(n, vector<int>(n));
for(int len = 1; len <= n; ++len)
for(int l = 0; l + len - 1 < n; ++l)
{
int r = len + l - 1;
if(len == 1) dp[l][r] = 1;
else {
if(s[l] == s[r]) dp[l][r] = dp[l+1][r-1] + 2;
else {
dp[l][r] = max(dp[l][r], max(dp[l+1][r], dp[l][r-1]));
}
}
}
int ans = 0;
for(auto &arr : dp)
for(auto &x : arr)
ans = max(ans, x);
return ans;
}
};
题意:Ural 大学有 N N N 名职员,编号为 1 ∼ N 1 \sim N 1∼N。他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。每个人有一个开心值,选出若干人,使得开心值最大,没有职员愿意和直接上司一起
Code:
#include
using namespace std;
const int N = 6010;
int e[N], ne[N], h[N], idx;
int happy[N], n, dp[N][2];
bool f[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs(int u)
{
dp[u][1] = happy[u];
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
dfs(j);
dp[u][0] += max(dp[j][0], dp[j][1]);
dp[u][1] += dp[j][0];
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++i) cin >> happy[i];
memset(h, -1, sizeof h);
for(int i = 1; i < n; ++i)
{
int a, b; cin >> a >> b;
f[a] = true;
add(b, a);
}
int root = 1;
while(f[root]) root++;
dfs(root);
cout << max(dp[root][1], dp[root][0]) << endl;
return 0;
}
数位 D P DP DP问题一般是给出一个区间 [ L , R ] [L,R] [L,R],问区间中满足给定条件的数的数量。解决办法就是前缀和求解: ∑ i = 1 R a n s i − ∑ i = 1 L − 1 a n s i \sum_{i=1}^{R}ans_i - \sum_{i=1}^{L-1} ans_i ∑i=1Ransi−∑i=1L−1ansi
int dfs(int pos, int pre, int lead, int limit){
if(!pos) { 边界 }
if(!limit && !lead && dp[pos][pre] != -1) return dp[pos][pre];
int res = 0;
int mx = limit ? a[pos] : 9;
for(int i = 0; i < mx; ++i)
{
if( 不符合的 ) continue;
res += dfs(pos - 1, , , limit && i == mx);
}
return limit ? res : dp[pos][pre] = res;
}
int cal(int x)
{
memset(dp, -1, sizeof dp);
len = 0;
while (x) a[++len] = x % 10, x /= 10;
return dfs(len, , 1 ,1 );
}
int main()
{
cin >> L >> R;
cout << cal(R) - cal(L - 1) << endl;
return 0;
}
初始化 d p dp dp数组为-1,数长度为0;
l e n len len 表示这个数的长度, a i a_i ai表示每个数位的具体数字
r e t u r n return return:需要根据题意做出判断
p o s pos pos表示数字的位数
l i m i t limit limit表示相关的限制
p r e pre pre表示前驱
$lead表示前导零是否存在
r e s res res记录答案, m x mx mx表示该位的最大值,此外采用了记忆化搜索。
if(!limit && !lead && dp[pos][pre] != -1) return dp[pos][pre];
return limit ? res : dp[pos][pre] = res;
题意:找到区间 [ L , R ] [L, R] [L,R]内不能出现4或62的次数
Code:
#include
using namespace std;
const int N = 15;
int l, r, dp[N][N], len, a[N];
//a[i]表示第i位的数,dp[i][j]表示第i位且前位是pre的满足数的个数
int dfs(int pos, int pre, bool limit)
{
if(!pos) return 1; // 枚举位数到头了返回1
if( !limit && dp[pos][pre] != -1) return dp[pos][pre];
// 记忆化搜索,没有限制并且这个被搜过了就直接返回即可
int res = 0;
int mx = limit ? a[pos] : 9; // 判断是否受限
for(int i = 0; i <= mx; ++i)
{
// 不满足题意得
if(i == 4 || ( i == 2 && pre == 6)) continue;
res += dfs(pos - 1, i, limit && i == mx);
// 枚举下一位,且前一位是i,如果当前有限制且到达了最大,那么下一位也将受限
}
return limit ? res : dp[pos][pre] = res;
}
int cal( int x )
{
memset(dp, -1, sizeof dp); // 初始化
len = 0; // 获取该数的每一位
while ( x ) a[ ++ len] = x % 10 , x /= 10;
return dfs(len, 0, 1);
}
int main()
{
while(cin >> l >> r, l && r)
cout << cal(r) - cal(l - 1) << endl;
return 0;
}
题意:求区间 [ L , R ] [L, R] [L,R]之间相邻数字只差不小于 2 2 2的个数
Code:
#include
using namespace std;
const int N = 15;
int l, r, dp[N][N], len, a[N];
int dfs(int pos, int pre, int lead, bool limit)
{
if(!pos) return 1;
if( !limit && !lead && dp[pos][pre] != -1) return dp[pos][pre];
int res = 0;
int mx = limit ? a[pos] : 9;
for(int i = 0; i <= mx; ++i)
{
if(abs(pre - i) < 2) continue;
if(lead && !i) { // 继续往下找到符合数字的第一位
res += dfs(pos - 1, -2, 1, limit && i == mx);
}
else {
res += dfs(pos - 1, i, 0, limit && i == mx);
}
}
return limit ? res : dp[pos][pre] = res;
}
int cal( int x )
{
memset(dp, -1, sizeof dp);
len = 0;
while ( x ) a[ ++ len] = x % 10 , x /= 10;
return dfs(len, -2, 1, 1);
}
int main()
{
cin >> l >> r;
cout << cal(r) - cal(l - 1) << endl;
return 0;
}
题意:计算 [ L , R ] [L, R] [L,R] 各位数字不下降的个数
Code:
#include
using namespace std;
const int N = 15;
int l, r, dp[N][N], len, a[N];
int dfs(int pos, int pre, bool limit)
{
if(!pos) return 1;
if( !limit && dp[pos][pre] != -1) return dp[pos][pre];
int res = 0;
int mx = limit ? a[pos] : 9;
for(int i = 0; i <= mx; ++i)
{
if(i < pre) continue;
res += dfs( pos - 1, i, limit && i == mx);
}
return limit ? res : dp[pos][pre] = res;
}
int cal( int x )
{
memset(dp, -1, sizeof dp);
len = 0;
while ( x ) a[ ++ len] = x % 10 , x /= 10;
return dfs(len, 0, 1);
}
int main()
{
while(cin >> l >> r)
cout << cal(r) - cal(l - 1) << endl;
return 0;
}
题意:计算 [ L , R ] [L, R] [L,R]每个数位之和 m o d p mod p modp n n n == 0 的个数
Code:
#include
using namespace std;
const int N = 110;
int l, r, dp[N][N], len, a[N], n;
int dfs(int pos, int sum, bool limit)
{
if(!pos) return sum % n == 0;
if( !limit && dp[pos][sum] != -1) return dp[pos][sum];
int res = 0;
int mx = limit ? a[pos] : 9;
for(int i = 0; i <= mx; ++i)
{
res += dfs( pos - 1, sum + i, limit && i == mx);
}
return limit ? res : dp[pos][sum] = res;
}
int cal( int x )
{
if(!x) return 1;
memset(dp, -1, sizeof dp);
len = 0;
while ( x ) a[ ++ len] = x % 10 , x /= 10;
return dfs(len, 0, 1);
}
int main()
{
while(cin >> l >> r >> n)
cout << cal(r) - cal(l - 1) << endl;
return 0;
}
题意:计算 [ L , R ] [L, R] [L,R]恰好为 K K K个 B B B的幂次方之和的个数,并且这些书都不相同
Code:
#include
using namespace std;
const int N = 35;
int l, r, dp[N][N], len, a[N], k , b;
int dfs(int pos, int sum, bool limit)
{
if(!pos) return sum == k;
if( !limit && dp[pos][sum] != -1) return dp[pos][sum];
int res = 0;
int mx = limit ? a[pos] : b - 1;
for(int i = 0; i <= mx; ++i)
{
//如果前面系数不为1或者以及到达k个就跳过
if( (i==1 && sum == k)|| i > 1) continue;
res += dfs(pos - 1, sum + ( i == 1), limit && i == mx);
}
return limit ? res : dp[pos][sum] = res;
}
int cal( int x )
{
memset(dp, -1, sizeof dp);
len = 0;
while ( x ) a[ ++ len] = x % b , x /= b;
return dfs(len, 0, 1);
}
int main()
{
while(cin >> l >> r >> k >> b)
cout << cal(r) - cal(l - 1) << endl;
return 0;
}
题意:计算 [ L , R ] [L, R] [L,R]之间出现$0 \sim 9 $的个数
Code:
#include
using namespace std;
const int N = 35;
int l, r, dp[N][N], len, a[N];
int dfs(int pos, int sum, int num, bool lead, bool limit)
{
if(!pos) {
if(lead && !num) return 1;
return sum;
}
if( !limit && !lead && dp[pos][sum] != -1) return dp[pos][sum];
int res = 0;
int mx = limit ? a[pos] : 9;
for(int i = 0; i <= mx; ++i)
{
int t;
if( i == num) {
if(!num) t = sum + (lead == 0);
else t = sum + 1;
}
else t = sum;
res += dfs(pos - 1, t, num, lead && i == 0, limit && i == mx);
}
return limit ? res : (lead ? res : dp[pos][sum] = res);
}
int cal( int x, int num)
{
memset(dp, -1, sizeof dp);
len = 0;
while ( x ) a[ ++ len] = x % 10 , x /= 10;
return dfs(len, 0,num, 1, 1);
}
int main()
{
while(cin >> l >> r, l && r) {
if(l > r) swap(l ,r);
for(int i = 0; i < 10; ++i)
cout << cal(r, i) - cal(l-1, i) << " \n"[i == 9];
}
return 0;
}
题意:求出 [ L , R ] [L, R] [L,R]之间各位数字之和能整除原书的个数
Sol:套用数位DP模板, m o d mod mod表示各位数和, s u m sum sum表示原数,发现这样的话 s u m sum sum原数将达到 1 0 18 10^{18} 1018,记忆化数组存不下,故我们考虑枚举各位数和,即模数
Code:
#include
using namespace std;
typedef long long LL;
LL l, r,len, dp[20][200][200], a[20], Mod;
LL dfs( int pos, int mod, LL sum, bool limit)
{
if(!pos) {
return (sum == 0 && mod == Mod);
}
if( !limit && dp[pos][mod][sum] != -1) return dp[pos][mod][sum];
LL res = 0;
int mx = limit ? a[pos] : 9;
for(int i = 0; i <= mx; ++i)
res += dfs( pos - 1, (mod + i),( sum * 10 + i) % Mod, limit && i == mx);
return limit ? res : dp[pos][mod][sum] = res;
}
LL cal( LL x )
{
len = 0;
while (x) a[ ++len] = x % 10, x /= 10;
LL ans = 0;//枚举模数,即各位数和
for( Mod = 1; Mod <= 9 * len; ++Mod)
{
memset(dp, -1, sizeof dp);
ans += dfs(len, 0, 0, 1);
}
return ans;
}
int main()
{
cin >> l >> r;
cout << cal(r) - cal(l-1) << endl;
return 0;
}
题意:计算 [ L , R ] [L, R] [L,R]之间各数的非零数不超过三个
Sol:套模板即可
#include
using namespace std;
typedef long long LL;
LL l, r,len, dp[20][20], a[20];
LL dfs( int pos, int sum, bool lead, bool limit)
{
if(!pos) {
return 1;
}
if( !limit && !lead && dp[pos][sum] != -1) return dp[pos][sum];
LL res = 0;
int mx = limit ? a[pos] : 9;
for(int i = 0; i <= mx; ++i)
{
if(i != 0 && sum >= 3) continue;
res += dfs(pos - 1, sum + (i != 0), lead && i == 0, limit && i == mx);
}
return limit ? res : (lead ? res : dp[pos][sum] = res);
}
LL cal( LL x )
{
len = 0;
while (x) a[ ++len] = x % 10, x /= 10;
return dfs(len, 0, 1, 1);
}
int main()
{
int T; cin >> T;
memset(dp, -1, sizeof dp);
while ( T -- )
{
cin >> l >> r;
cout << cal(r) - cal(l-1) << endl;
}
return 0;
}
题意: s u m i sum_i sumi表示 i i i的二进制中1的个数,求 ∏ i = 1 n s u m i \prod_{i=1}^{n}sum_i ∏i=1nsumi
Sol:我们定义 G k G_k Gk为 ∑ i = 1 n ( s u m i = = k ) \sum_{i=1}^n (sum_i == k) ∑i=1n(sumi==k),易知答案就为: ∏ i = 1 50 i G i \prod_{i=1}^{50} i^{G_i} ∏i=150iGi,利用数位dp求出 1 ∼ n 1 \sim n 1∼n的 G i G_i Gi,然后快速幂计算
Code:
#include
using namespace std;
typedef long long LL;
const LL mod =10000007;
LL x,len, dp[64][64], a[64], G[64], P;
LL qpow( LL a, LL b)
{
LL res = 1;
while(b) {
if(b & 1) res = res * a % mod;
b >>= 1;a = a * a % mod;}
return res;
}
LL dfs( int pos, int sum, bool limit)
{
if(!pos) {
return sum == P;
}
if( !limit && dp[pos][sum] != -1) return dp[pos][sum];
LL res = 0;
int mx = limit ? a[pos] : 1;
for(int i = 0; i <= mx; ++i)
{
if(sum > P) continue;
res += dfs(pos - 1, sum + (i != 0), limit && i == mx);
}
return limit ? res : dp[pos][sum] = res;
}
LL cal( LL x )
{
len = 0;
while (x) a[ ++len] = x % 2, x /= 2;
for( P = 1; P < 50; ++P)
{
memset(dp, -1, sizeof dp);
G[P] = dfs(len, 0, 1);
}
LL ans = 1;
for(int i = 1; i < 50; ++i)
ans = ans * qpow(i, G[i])% mod;
return ans % mod;
}
int main()
{
cin >> x;
cout << cal(x) << endl;
return 0;
}
子问题:不要666
Sol:一个数分为 a + b a+b a+b,例如: 82342 = 80000 + 2342 82342 = 80000+2342 82342=80000+2342
( a + b ) 3 = a 3 + b 3 + 3 ∗ a 2 ∗ b + 3 ∗ a ∗ b 2 (a+b)^3 = a^3 + b^3+3*a^2*b+3*a*b^2 (a+b)3=a3+b3+3∗a2∗b+3∗a∗b2
满足条件的数, c n t cnt cnt为这些数字的个数: ( a 3 ∗ c n t + ( b 3 + c 3 + ⋅ ⋅ ⋅ ) + 3 ∗ a 2 ∗ ( b + c + ⋅ ⋅ ⋅ ) + 3 ∗ a ∗ ( b 2 + c 2 + ⋅ ⋅ ⋅ ) ) (a^3*cnt + (b^3 + c^3 + ···) + 3 * a^2*(b+c+···) + 3*a*(b^2 + c^2+···)) (a3∗cnt+(b3+c3+⋅⋅⋅)+3∗a2∗(b+c+⋅⋅⋅)+3∗a∗(b2+c2+⋅⋅⋅))
数位 d p dp dp维护其个数,总和,平方和,立方和
Code:
#include
using namespace std;
typedef long long LL;
const int N = 30, mod = 1e9+7;
LL l, r, len, a[N], ten[N];
struct s
{
LL x, sum, sqr, cub;
} dp[20][10][10];
s dfs(int pos, int mod1, int mod2, bool limit ){
if(!pos) return (s){mod1&&mod2, 0, 0, 0};
if( !limit && dp[pos][mod1][mod2].x != -1) return dp[pos][mod1][mod2];
int mx = limit ? a[pos] : 9;
s res = {0, 0, 0, 0};
for(int i = 0; i <= mx ; ++i)
{
if( i == 6) continue;
s t = dfs(pos - 1, (mod1 + i) % 6, (mod2 * 10 + i) % 6, limit && i == mx);
LL d = (LL)i * ten[pos] % mod;
res.x = (res.x + t.x) % mod;
res.sum = (res.sum + d * t.x % mod + t.sum) % mod;
res.sqr = (res.sqr + t.x * d % mod * d % mod + 2LL * d % mod * t.sum % mod + t.sqr) % mod;
res.cub = (res.cub + t.x * d % mod * d % mod * d % mod + 3LL * d % mod * d % mod * t.sum % mod + 3LL * d % mod * t.sqr % mod + t.cub) % mod;
}
return limit ? res : dp[pos][mod1][mod2] = res;
}
LL cal(LL x)
{
len = 0;
while ( x ) a[ ++len] = x % 10, x /= 10;
return dfs(len, 0, 0 , 1).cub;
}
int main()
{
memset(dp, -1, sizeof dp);
ten[1] = 1;
for(int i = 2; i <= 20; ++i) ten[i] = ten[i-1]*10;
while (cin >> l >> r )
cout << (cal(r) - cal(l - 1)+mod) % mod << endl;
return 0;
}