X进制减法 (数论,推公式,贪心)
这道题的难点在于知道题目中的321如何转化为65的
3 2 1 = 1 * 1 + 2 * 2 + 3 * 10 * 2 = 65
知道这一点后,为求A-B的最小值,让每一位取到尽可能小的进制最后就是最小值
若要求10进制的1234从大到小可以这么求
1234
v=0
v=v * 10+1
v=v * 10+2
v=v * 10+3
v=v * 10+4
v=1234
同理对于X进制也同样适用
核心代码
for(int i = 0;i < ma;++i){
cin >> a[i];
}
cin >> mb;
for(int i = 0;i < mb;++i){
cin >> b[i];
}
reverse(a,a+ma);
reverse(b,b+mb);
for(int i = 0;i < max(ma,mb);++i){
c[i] = max(max(a[i],b[i])+1,2ll);
}
long long A = 0,B = 0;
for(int i = ma-1;i >= 0;--i){
A = ((A * c[i]) + a[i]) % mod;
}
for(int i = mb-1;i >= 0;--i){
B = ((B * c[i]) + b[i]) % mod;
}
cout << (A - B + mod) % mod;//正确计算 A - B 的非负余数
统计子矩阵 (前缀和、双指针)
这道题暴力做法就是求出前缀和数组后暴力枚举所有的点
这样做肯定会超时
优化方案为:求出前缀和数组后利用快慢指针,求出所有可能的值
核心代码
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
cin >> a[i][j];
a[i][j] += a[i-1][j]+a[i][j-1]-a[i-1][j-1];//前缀和
}
}
for(int i = 1;i <= m;i++){
for(int j = i;j <= m;j++){
for(int s = 1,t = 1;t <= n;t++){
while(s <= t && a[t][j] - a[s-1][j] - a[t][i-1] + a[s-1][i-1] > k)s++;//如果<k了,说明i、j、s、t围成的矩阵及其子矩阵都符合条件
if(s <= t) ans += t - s + 1;//累加到答案
}
}
}
cout << ans << '\n';
李白打酒加强版 (DP)
f[i][j][k]表示,还剩i家酒馆,j个花,k斗酒时,有多少种可能
核心代码
f[0][0][2] = 1;
for(int i = 0;i <= n;i++){
for(int j = 0;j <= m;j++){
for(int k = 0; k < m;k++){
//如果上一次是酒馆,那么会乘2,也就是说会被2整除
if(i && k % 2 == 0) f[i][j][k] = (f[i][j][k]+f[i-1][j][k/2])%MOD;
//如果上一次是花则不会
if(j) f[i][j][k] = (f[i][j][k]+f[i][j-1][k+1])%MOD;
}
}
}
cout << f[n][m-1][1]<<endl;