AtCoder Beginner Contest 315 题解
D , F , G D,F,G D,F,G 题解
D - Magical Cookies
题意
给定一个 h h h × w w w 的小写字母字符矩阵,现在一次执行如下操作:
- 对于每一行,如果这一行剩下两个以上的字符,并且全部相同,删除这一行全部的字符。同时每一列,如果这一列剩下两个以上的字符,并且全部相同,删除这一列全部的字符。
- 如果第 1 1 1 步有删除字符,跳转到第 1 1 1 步,否则结束。
求出最后剩下几个字符?
解法
一轮一轮进行删除操作,最多只进行 ( h + w ) (h + w) (h+w) 轮操作;
对于每轮操作,需要检查所有行与列,即 ( h + w ) (h + w) (h+w) ;
每一次检查,只需要枚举 26 26 26 种小写字母即可.
#include <bits/stdc++.h>
#define For(i, a, b) for (ll i = a; i <= b; i++)
#define _For(i, a, b) for (ll i = a; i >= b; i--)
typedef long long ll;
using namespace std;
const int maxn = 2e3 + 5;
int n, m;
int cntx[30][maxn], cnty[30][maxn], resx[maxn], resy[maxn];
void solve() {
cin >> n >> m;
int ansx = n, ansy = m;
For (i, 1, n) {
For (j, 1, m) {
char c;
cin >> c;
cntx[c - 'a'][i]++;
cnty[c - 'a'][j]++;
}
}
For (i, 1, n) resx[i] = m;
For (i, 1, m) resy[i] = n;
For (loop, 1, n + m) {
vector <pair<int, int>> delx;
vector <pair<int, int>> dely;
For (i, 1, n) {
if (!resx[i]) continue;
For (l, 0, 25) {
if (cntx[l][i] == resx[i] && resx[i] > 1) {
ansx--;
resx[i] = 0;
cntx[l][i] = 0;
delx.push_back({i, l});
}
}
}
For (i, 1, m) {
if (!resy[i]) continue;
For (l, 0, 25) {
if (cnty[l][i] == resy[i] && resy[i] > 1) {
ansy--;
resy[i] = 0;
cnty[l][i] = 0;
dely.push_back({i, l});
}
}
}
for (auto i: delx) {
int x = i.first, c = i.second;
For (i, 1, m) {
resy[i]--, cnty[c][i]--;
}
}
for (auto i: dely) {
int y = i.first, c = i.second;
For (i, 1, n) {
resx[i]--, cntx[c][i]--;
}
}
}
cout << ansx * ansy;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t;
t = 1;
while (t--) {
solve();
}
return 0;
}
F - Shortcuts
题意
在一个二维平面上有 n n n 个点,你需要按顺序从点 1 1 1 走到点 n n n 。
你可选择跳过 c c c 个点不走(不能跳过起点和终点),跳过的惩罚是 $2^{c-1} $ (特别的, c = 0 c = 0 c=0 时,惩罚为 0 0 0 )。
求出走的距离加上惩罚的最小值。
解法
在跳过 30 30 30 个点后,惩罚已经有 1 0 9 10^9 109 ,已经超过了距离和,所以最多可跳点数不超过 k = 30 k = 30 k=30 个点;
动态规划,设 d p [ i ] [ j ] dp[i][j] dp[i][j] 为到达 i i i ,已经跳过了 j j j 个节点的最小距离(不包括惩罚);
递推式为 d p [ i + j + l + 1 ] [ j + l ] = m i n ( d p [ i + j + l + 1 ] [ j + l ] , d p [ i + l ] [ l ] + s q r t ( d i s ) ) dp[i + j + l + 1][j + l] = min(dp[i + j + l + 1][j + l], dp[i + l][l] + sqrt(dis)) dp[i+j+l+1][j+l]=min(dp[i+j+l+1][j+l],dp[i+l][l]+sqrt(dis)) .
#include <bits/stdc++.h>
#define For(i, a, b) for (ll i = a; i <= b; i++)
#define _For(i, a, b) for (ll i = a; i >= b; i--)
typedef long long ll;
using namespace std;
const int maxn = 1e4 + 5;
int n;
void solve() {
cin >> n;
vector <pair<int, int>> a(n + 1);
For (i, 1, n) cin >> a[i].first >> a[i].second;
vector <vector <double> > dp(n + 1,vector <double> (35, 1e10));
dp[1][0] = 0;
For (i, 1, n) {
For (j, 0, 30) {
For (l, 0, 30) {
if (j + l > 30 || i + j + l + 1 > n) break;
double dis = (a[i + l + j + 1].second - a[i + l].second) * (a[i + l + j + 1].second - a[i + l].second) + (a[i + l + j + 1].first - a[i + l].first) * (a[i + l + j + 1].first - a[i + l].first);
dp[i + j + l + 1][j + l] = min(dp[i + j + l + 1][j + l], dp[i + l][l] + sqrt(dis));
}
}
}
double ans = 1e10;
For (i, 0, 30) {
ans = min(ans, dp[n][i] + (i == 0 ? 0 : pow(2, i - 1)));
}
cout << fixed << setprecision(20) << ans;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t;
t = 1;
while (t--) {
solve();
}
return 0;
}
G - Ai + Bj + Ck = X (1 <= i, j, k <= N)
题意
求出 A i + B j + C k = X Ai + Bj + Ck = X Ai+Bj+Ck=X 的整数解 ( i , j , k ) (i, j, k) (i,j,k) 的个数(要求 1 ⩽ i , j , k ⩽ N 1 \leqslant i,j,k \leqslant N 1⩽i,j,k⩽N ).
解法
模板题
前置知识: 拓展欧几里得算法
考虑子问题:求出 a x + b y = c ax + by = c ax+by=c 的整数解 ( x , y ) (x, y) (x,y) 的个数.
解答:当 c c c ∤ \nmid ∤ g c d ( a , b ) gcd(a, b) gcd(a,b) 时,方程无解。否则可通过拓展欧几里得求出一组解 x 0 , y 0 x_0, y_0 x0,y0 。其他所有解为 x = x 0 + k b g c d ( a , b ) , y = y 0 − k a g c d ( a , b ) ( k ∈ Z ) x = x_0 + \frac{kb}{gcd(a, b)}, y = y_0 - \frac{ka}{gcd(a, b)} (k\in \mathbb{Z}) x=x0+gcd(a,b)kb,y=y0−gcd(a,b)ka(k∈Z) 。可以通过解不等式求出 k k k 的范围。
对于本题,枚举 x x x 在 1 ∼ N 1 \sim N 1∼N 中的每一个值,然后利用前置知识求出 j , k j, k j,k 的可行解数量,最后加在一起。复杂度 O ( n log n ) O(n\log n) O(nlogn) 。
#include <bits/stdc++.h>
#define For(i, a, b) for (ll i = a; i <= b; i++)
#define _For(i, a, b) for (ll i = a; i >= b; i--)
typedef long long ll;
using namespace std;
/**
* @brief exgcd 求方程 ax + by = gcd(a, b) 的一组整数解
* @return 返回 gcd(a, b)
*/
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (!b) {
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
void solve() {
ll n, a, b, c, X;
cin >> n >> a >> b >> c >> X;
ll ans = 0;
For (i, 1, n) {
ll k = X - a * i;
ll x, y;
ll d = exgcd(b, c, x, y);
ll tx = c / d, ty = b / d;
if (k % d) continue;
x = (k / d % tx * x % tx + tx) % tx;
y = (k - b * x) / c;
ll l1, l2, r1, r2;
l1 = ceil(1.0 * (1ll - x) / tx);
l2 = ceil(1.0 * (y - n) / ty);
r1 = floor(1.0 * (n - x) / tx);
r2 = floor(1.0 * (y - 1ll) / ty);
l1 = max(l1, l2);
r1 = min(r1, r2);
if (r1 >= l1)
ans += r1 - l1 + 1;
}
cout << ans;
/*
ll a, b, k, x, y;
// 求 ax + by = k 的解
cin >> a >> b >> k;
ll d = exgcd(a, b, x, y);
ll tx = b / d, ty = a / d;
if (k % d) { puts("No Solution"); return; }
else {
x = x * k / d;
// x = (k / d % tx * x % tx + tx) % tx;
y = (k - a * x) / b;
根据拓展欧几里得求出的一组解,x0, y0
其他解为 x = x0 + k * b / gcd(a, b), y = y0 - k * a / gcd(a, b)
(k ∈ Z)
}
*/
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t;
t = 1;
while (t--) {
solve();
}
return 0;
}
参考
[1]: https://zhuanlan.zhihu.com/p/651098768