AtCoder Beginner Contest 182
导读:
简单的题目,只说明题意,或者直接说明结论
下面的难题,会做详细的解释和证明
立个flag,在座的大佬们做个见证:一个月刷60场ABC,现在2021/7/7,第二十二天,已打卡十八场。
F - Valid payments
题意:有n中面值的货币a[i],其中满足a[1] = 1, a[i+1]都是a[i]的倍数。
现在有一种价格为x的商品,付款y元,找零y-x元
问满足一下两种条件的方案数为多少
1.付款和找零的货币数量在对应金额下为最少
2.付款时使用的货币面值在找零的时候就不可以在使用。
解决:
首先对于条件1,我们可以得出每个面值的货币最大的使用数量,也就是mx[i] = a[i + 1] / a[i]
证明,假设某种货币i使用次数大于mx[i]的时候,我们可以用a[i + 1]来代替。
其次,每个面值都可以被唯一的表达,上面的论证同样可以证明这里的作用。
那么,我们可以将x+b=y表示为:
b1a1 + b2a2 + … + bnan
+x1a1 + x2a2 + … + xnan
=y1a1 + y2a2 + … + ynan
其中,有条件2可知。如果bi为非0数,那么yi一定为0,如果bi为0,yi可以为0,也可以不为0.要保证bi和yi不同时为非0数。
因为x是唯一确定的,所以,如果b确定了,那么y也一定是确定的,所以,询问有多少中方案数,也就是问可以组成多少个合法的b或者合法的y,
可以用DP解决,dp[i][0/1]
表示当前位i是否进位(0表示不进位)的b的方案数
如果当前位没有进位,那么,
1.第i+1位一定可以不进位(如果我让b[i]为0,就不用进位了)
2.若第i+1位想进位,一定要让x为正值,让b[i + 1]值为mx[i]-1就可以了
如果当前位有进位,那么
1.第i+1位一定可以进位,(因为i位有进位,我让b[i + 1]为mx[i]-1就可以了
2.若第i+1为想不进位,一定要让x[i + 1] + 1小于mx[i + 1]。(让b[i+1]等于0就可以了)
下面是代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 55;
ll n, x;
ll a[N];
ll mx[N], xx[N];
ll dp[N][2];
int main()
{
cin >> n >> x;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 1; i < n; i ++ ) mx[i] = a[i + 1] / a[i];
for (int i = n; i >= 1; i -- )
{
xx[i] = x / a[i];
x %= a[i];
}
dp[1][0] = 1;
if (xx[1]) dp[1][1] = 1;
for (int i = 1; i < n; i ++ )
{
dp[i + 1][0] += dp[i][0];
dp[i + 1][1] += dp[i][1];
if (xx[i + 1]) dp[i + 1][1] += dp[i][0];
if (xx[i + 1] + 1 != mx[i + 1]) dp[i + 1][0] += dp[i][1];
}
cout << dp[n][0] << endl;
return 0;
}
E - Akari
暴力模拟
#include <bits/stdc++.h>
using namespace std;
const int N = 1505;
int n, m;
int g[N][N];
bool st[N][N];
bool check(int x, int y)
{
if (g[x][y] == 1) return false;
if (g[x][y] == -1) return false;
if (x == 0 || x > n || y == 0 || y > m) return false;
return true;
}
int main()
{
int A, B;
cin >> n >> m >> A >> B;
vector<pair<int, int>> v;
while (A -- )
{
int x, y; scanf("%d%d", &x, &y);
g[x][y] = 1;
v.push_back({x, y});
}
sort(v.begin(), v.end());
while (B -- )
{
int x, y; scanf("%d%d", &x, &y);
g[x][y] = -1;
}
for (int i = 0; i < v.size(); i ++ )
{
int x = v[i].first, y = v[i].second;
st[x][y] = true;
int a = x + 1, b = y;
while (check(a, b)) st[a ++ ][b] = true;
a = x, b = y + 1;
while (check(a, b)) st[a][b ++ ] = true;
a = x - 1, b = y;
while (check(a, b)) st[a --][b] = true;
a = x, b = y - 1;
while (check(a, b)) st[a][b -- ] = true;
}
int ans = 0;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
if (st[i][j]) ans ++;
cout << ans << endl;
return 0;
}
D - Wandering
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200005;
int n;
ll a[N], s[N];
int main()
{
cin >> n;
ll mx = -1e18;
ll sum = 0, ans = 0;
ll tmp[N];
ll val = 0;
for (int i = 1; i <= n; i ++ )
{
scanf("%lld", &a[i]);
s[i] = s[i - 1] + a[i];
mx = max(s[i], mx);
tmp[i] = mx;
sum += s[i];
ans = max(val + tmp[i], ans);
val = sum;
}
cout << max(ans, (ll)0) << endl;
return 0;
}
C - To 3
void work()
{
LL n; cin >> n;
int a[3] = {0};
while (n)
{
int t = (n % 10) % 3;
if (n % 10 != 0 && t == 0) a[0] ++;
if (t) a[t] ++;
// cout << n % 3 << endl;
n /= 10;
}
int sum = a[1] + a[2] * 2;
// cout << sum << endl;
// cout << a[0] << " " << a[1] << " " << a[2] << endl;
if (sum % 3 == 0)
cout << 0 << endl;
else if (sum % 3 == 1)
{
if (a[1] && (a[2] || a[0])) cout << 1 << endl;
else if (a[2] > 1 && (a[0] || a[1])) cout << 2 << endl;
else cout << -1 << endl;
}
else if (sum % 3 == 2)
{
// cout << a[0] << " " << a[1] << " " << a[2] << endl;
if (a[2] && (a[0] || a[1])) cout << 1 << endl;
else if (a[1] > 1 && (a[0] || a[2])) cout << 2 << endl;
else cout << -1 << endl;
}
}
B - Almost GCD
void work()
{
int n; cin >> n;
int p[1005] = {0};
for (int i = 1; i <= n; i ++ )
{
int x; cin >> x;
for (int j = 2; j <= x / j; j ++ )
if (x % j == 0)
{
p[j] ++;
while (x % j == 0) x /= j;
}
if (x > 0) p[x] ++;
}
int ans = 0, cnt = 0;
for (int i = 2; i <= 1000; i ++ ) if (p[i] > cnt) ans = i, cnt = p[i];
cout << ans << endl;
}
A - twiblr
void work()
{
int a, b; cin >> a >> b;
cout << 2 * a + 100 - b << endl;
}