2024 暑假友谊赛-热身1(7.11)
AtCoder abc079_d
思路:floyd跑最短路(赛时把循环顺序记错了🤡)
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;
int c[10][10];
void solve() {
int h, w;
cin >> h >> w;
for (int i = 0; i <= 9; ++i) {
for (int j = 0; j <= 9; ++j) {
cin >> c[i][j];
}
}
for (int k = 0; k <= 9; ++k) {
for (int i = 0; i <= 9; ++i) {
for (int j = 0; j <= 9; ++j) {
if (c[i][k] + c[k][j] < c[i][j]) {
c[i][j] = c[i][k] + c[k][j];
}
}
}
}
int ans = 0;
for (int i = 1; i <= h; ++i) {
for (int j = 1; j <= w; ++j) {
int x;
cin >> x;
if (x != -1) ans += c[x][1];
}
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}
AtCoder arc100_a
思路:求中位数的变形,排个序取中位数就行了
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
int sum = 0;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
a[i] -= i;
sum += a[i];
}
sort(a.begin() + 1, a.end());
int x = n / 2, ans = LLONG_MAX;
for (int i = max(1ll, x - 100); i <= min(n, x + 100); ++i) {
int b = a[i], cnt = 0;
for (int j = 1; j <= n; ++j) {
cnt += abs(a[j] - b);
}
ans = min(ans, cnt);
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}
AtCoder arc099_a
思路:给的是个排列,最小值就是1,就是要全部变为1,模拟过程求数量
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;
void solve() {
int n, k;
cin >> n >> k;
vector<int> a(n + 5);
int pos;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
if (a[i] == 1) pos = i;
}
int l = 1, cnt = 1;
while (l + k <= n) {
cnt ++, l += k - 1;
}
cout << cnt;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}
AtCoder arc100_b
思路:这里用的是二分,先确定第二个分割点,左右两边在分别求一个分割点
要使得差最小,应该是保证分割点两边的和是尽可能接近的,去二分前缀和中接近sum/2的位置,枚举附近的数为分割点,取最小值
确定了第二个分割点后,也可以用双指针维护第一和第三个分割点
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;
void solve() {
int n;
cin >> n;
vector<int> a(n + 1), sum(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
int ans = LLONG_MAX;
for (int i = 2; i + 2 <= n; ++i) {
// cout << "i= "<< i << ':' << '\n';
int suml = sum[i], sumr = sum[n] - sum[i];
auto pl = std::lower_bound(sum.begin(), sum.end(), suml / 2);
int pll = pl - sum.begin();
// cout << "p: " << pll << '\n';
int dl = LLONG_MAX, tl;
for (int j = max(1ll, pll - 3); j <= pll + 3 && j + 1 <= i; ++j) {
// cout << " j: " << j << " ";
// cout << sum[j] << ' ' << sum[i] - sum[j] << '\n';
int dd = abs(sum[j] - (sum[i] - sum[j]));
if (dd < dl) {
tl = j;
dl = dd;
}
}
auto pr = std::lower_bound(sum.begin(), sum.end(), sum[i] + (sumr / 2));
int prr = pr - sum.begin();
int dr = LLONG_MAX, tr;
for (int j = max(i + 1, prr - 3); j <= prr + 3 && j + 1 <= n; ++j) {
int dd = abs(sum[j] - sum[i] - (sum[n] - sum[j]));
if (dd < dr) {
tr = j;
dr = dd;
}
}
int ma = max({sum[tl], sum[i] - sum[tl], sum[tr] - sum[i], sum[n] - sum[tr]});
int mi = min({sum[tl], sum[i] - sum[tl], sum[tr] - sum[i], sum[n] - sum[tr]});
ans = min(ans, ma - mi);
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}
CodeForces 1808C
思路:
数位dp的思想进行搜索
首先是确定当前位,以及这一位是否是左右边界,以及当前最小最大值
然后一位一位的搜
还有一个思路就是暴力枚举最小最大值,判断是否存在范围内的数满足条件
在已知最小最大值的情况下,如何判断当前位上的数是否可行:
不大于右边界:将当前位后的所有数变成最小值后不大于有边界
不小于左边界:将当前位后的所有数变成最大值后不小于左边界
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e5 + 5, mod = 998244353;
void solve() {
string ls, rs;
cin >> ls >> rs;
int l, r;
l = stoll(ls), r= stoll(rs);
int len = 10;
if (l == r) {
cout << l << '\n';
return ;
}
if (ls.size() != rs.size()) {
for (int i = 0; i < ls.size(); ++i) cout << '9';
cout << '\n';
return;
}
string ans;
auto P = [=] (int mi, int ma) -> pair<int, string>{
if (mi == ma && mi == 0) return {0, ""};
string t(ls.size(), '0');
for (int i = 0; i < t.size(); ++i) {
bool is = false;
for (int j = mi; j <= ma; ++j) {
string tr = t;
tr[i] = j + '0';
for (int k = i + 1; k < t.size(); ++k) tr[k] = mi + '0';
if (tr > rs) {
return {0, ""};
}
string tl = t;
tl[i] = j + '0';
for (int k = i + 1; k < t.size(); ++k) tl[k] = ma + '0';
if (tl < ls) continue;
is = true;
t[i] = j + '0';
break;
}
if (!is) return {0, ""};
}
return {1, t};
};
for (int i = 0; i < 10; ++i) {
for (int j = i; j < 10; ++j) {
auto [ok, s] = P(i, j);
if (ok && abs(i - j) < len) {
len = abs(i - j), ans = s;
}
}
}
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T --) {
solve();
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e5 + 5, mod = 998244353;
string ls, rs;
int l, r, n;
string ans, te;
int len = 10;
void dfs(int now, int st, int ed, int mi, int ma) {
if (ma - mi >= len) return;
if (now == n - 1) {
len = ma - mi;
ans = te;
return ;
}
char ll = '0', rr = '9';
if (st && ed) {
ll = ls[now + 1], rr = rs[now + 1];
} else if (st) {
ll = ls[now + 1];
} else if (ed) {
rr = rs[now + 1];
}
for (int i = ll; i <= rr; ++i) {
te.push_back(i);
dfs(now + 1, i == ll && st, i == rr && ed, min(mi, i - '0'), max(ma, i - '0'));
te.pop_back();
}
}
void solve() {
te.clear();
cin >> ls >> rs;
l = stoll(ls), r= stoll(rs);
n = ls.size();
ans = ls, len = 10;
if (l == r) {
cout << l << '\n';
return ;
}
if (ls.size() != rs.size()) {
for (int i = 0; i < ls.size(); ++i) cout << '9';
cout << '\n';
return;
}
for (int i = ls[0]; i <= rs[0]; ++i) {
te.push_back(i);
dfs(0, i == ls[0], i == rs[0],i - '0', i - '0');
te.pop_back();
}
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T --) {
solve();
}
return 0;
}
CodeForces 1547E
思路:由式子可以看出i的温度是和i-1和i+1有关系的
当i>aj时,式子为tj-aj+i,i的温度比i-1的温度多1;
当i<=aj时,式子为tj+aj-i,i的温度比i+1的温度多1
直接先标记每个a[j]位置的温度,左右分别两次传递所有a[j]带来的温度的最小值
下面的代码是维护的前缀tj-aj和后缀tj+aj
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 2e5 + 5;
struct E {
int a, t;
// bool operator<(const E&e)const {
// a < e.a;
// }
};
bool cmp(E x, E y) {
return x.a < y.a;
}
void solve() {
int n, k;
cin >> n >> k;
vector<E> ve(k + 1);
vector<int> pos(n + 1);
for (int i = 1; i <= k; ++i) cin >> ve[i].a;
for (int i = 1; i <= k; ++i) cin >> ve[i].t;
sort(ve.begin() + 1, ve.end(), cmp);
for (int i = 1; i <= k; ++i) {
pos[ve[i].a] = i;
}
vector<int> l(n + 1, 1e16), r(n + 2, 1e16);
for (int i = 1; i <= n; ++i) {
l[i] = l[i - 1];
if (pos[i]) l[i] = min(l[i], ve[pos[i]].t - ve[pos[i]].a);
}
for (int i = n; i >= 1; --i) {
r[i] = r[i + 1];
if (pos[i]) r[i] = min(r[i], ve[pos[i]].t + ve[pos[i]].a);
}
for (int i = 1; i <= n; ++i) {
cout << min(l[i - 1] + i, r[i] - i) << ' ';
}
cout << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
cin >> T;
while (T --) {
solve();
}
return 0;
}
CodeForces 1107C
思路:用单调队列维护下当前连续相同字符的数,直接统计
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;
void solve() {
int n, k;
cin >> n >> k;
vector<int> ve(n + 1);
for (int i = 1; i <= n; ++i) cin >> ve[i];
string s;
cin >> s;
priority_queue<int> q;
s = ' ' + s + ' ';
int ans = 0;
char pre = ' ';
for (int i = 1; i <= n + 1; ++i) {
if (s[i] != pre) {
int cnt = k;
while (cnt -- && q.size()) {
ans += q.top();
q.pop();
}
while (q.size()) q.pop();
}
q.push(ve[i]);
pre = s[i];
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}
AtCoder arc102_b
思路:
要求用不超过20个点,60条边,且L的范围为1e6
220刚好为1e6,就可以猜测跟二进制有关
对于最简单的情况,构建0~2k - 1的路径:
构造一条链,每个点按顺序表示二进制上的每一位,点i表示二进制上第i - 1位。
相邻的点之间连2条边,一条表示在二进制上为0(即边权为0),另一条表示在二进制上为1(即边权为2i)
现在的问题是要恰好有L条路径,如果按上面方法构造,路径数一定是2k - 1条,下面考虑用添加一些特殊边来构造
首先看一个二进制数10101,有5位,且最高位在4
若把第4位(最高位)变成0,把它后面都变成1,即变成01111,10101一定是包含0~01111的数的,且0~01111就可以直接用上述方法直接构造
对于剩下的10000~10101,等价于10000 + (0~00101),其中0~00101又可以同理的把第2位(最高位)变成0,把它后面的都变成1,即变成00011,所以0~00101等价于0~00011 和 00100~00101的并集,而其中的00100~00101又可以等价于00100 + (0~00001),0~00001也可以直接用上述方法直接构造
知道了原理进行建边,根据上述的构造方法,走到第i个点所包含的路径为0~2i-2,将i进行连边,相当于得到了权值为0~2i-2的路径,所以将每次等价出的2k-1通过连边来获取即可
struct E {
int u, v, w;
};
void solve() {
int L;
cin >> L;
vector<E> g;
int n, ma;
// n表示L二进制下的位数
// ma表示L的最高位
for (int i = 20; i >= 0; --i) {
if ((L >> i) & 1) {
ma = i;
n = i + 1;
break;
}
}
// 先构造0 ~ 2^(ma - 1) - 1的路径
// i节点连的边表示二进制第i位是0或1(从1开始)
for (int i = 1; i <= ma; ++i) {
g.push_back({i, i + 1, 1 << i - 1});
g.push_back({i, i + 1, 0});
}
int now = 1 << ma; // 当前边权
for (int i = n - 1; i >= 1; --i) {
if ((L >> i - 1) & 1) {
g.push_back({i, n, now});
// 节点i的路径有 0 ~ 2^(i - 2)
now += 1 << i - 1;
}
}
cout << n << ' ' << g.size() << '\n';
for (auto [u, v, w]:g) cout << u << ' ' << v << ' ' << w << '\n';
}