[Atcoder]AtCoder Beginner Contest 233 题解
前面几题简单,就放个思路吧
A - 10yen Stamp
cout << (m - n - 1) / 10 + 1 << endl;
B - A Reverse
reverse(c + l, c + r + 1);
C - Product
直接暴搜,每一个数组枚举选啥,注意会爆longlong,算a*b之前提前判断一下log(a)+log(b)和log(x)的关系
D - Count Interval
求和为k的子段,map存一下前缀和的计数,设当前值为sum,然后每次查找cnt[sum - k]
E - Σ[k=0..10100]floor(X/10k)
求x + x / 10 + x / 100 + ...
发现相当于每次x往左移动一个,然后列竖式加,最后每一位往上进位。求不进位的值的时候,每一位都是前缀和。
F - Swap and Sort
一开始算错了,以为冒泡排序次数要超过\(5\times 10^5\),然后想半天没想出来。
发现直接冒泡排序次数都是够的。
每个操作都可以看成一条边,然后连通块内的可以排成有序,显然如果\(a[i]\)和\(i\)不在同一个连通块里面,它不可能排成有序。
整个图是若干个连通块,但是我们只需要搜索树就行了,每次找到一个叶子\(i\),然后找\(p[i]\)在哪个位置,再把这个值交换过来就行了。
找叶子的顺序可以建出搜索树深度,按深度排序后从小到大完成每个值,这样就是自底向上的了,似乎直接dfs序也行。
#include <bits/stdc++.h>
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define Mid ((l + r) / 2)
using namespace std;
const int N = 2e5 + 1009;
int n, m, dth[N], seq[N], p[N];
struct node {
int x, id;
};
vector<node> ver[N], vv[N];
vector<int> ans;
void dfs(int x, int pre) {
for(auto tt : ver[x]) if(!dth[tt.x]) {
int y = tt.x;
dth[y] = dth[x] + 1;
vv[y].push_back({x, tt.id});
vv[x].push_back({y, tt.id});
dfs(y, x);
}
}
int solve(int x, int y, int pre) {
if(p[x] == y) {
return true;
}
for(auto tt : vv[x]) if(tt.x != pre) {
int z = tt.x;
int t = solve(z, y, x);
if(t == 1) {
swap(p[x], p[z]);
ans.push_back(tt.id);
return 1;
}
}
return 0;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> p[i];
}
cin >> m;
for(int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
ver[a].push_back({b, i});
ver[b].push_back({a, i});
}
for(int i = 1; i <= n; i++) if(dth[i] == 0) {
dth[i] = 1;
dfs(i, 0);
}
for(int i = 1; i <= n; i++) {
seq[i] = i;
}
sort(seq + 1, seq + 1 + n, [](const int a, const int b) {return dth[a] > dth[b];});
for(int i = 1; i <= n; i++) {
if(!solve(seq[i], seq[i], 0)) {
cout << "-1" << endl;
return 0;
}
}
if(ans.size() > 500000) {
cout << "-1\n";
return 0;
}
cout << ans.size() << endl;
for(auto x : ans) {
cout << x << " " ;
}
cout << endl;
return 0;
}
G - Strongest Takahashi
给定一张图,求使用边长和最小为多少的正方形,盖住图上所有污渍。
直接dp就行了,状态为:覆盖一个子矩形需要几个正方形,然后枚举水平或垂直线,切成两部分分别计算。
如果是正方形,有一个额外转移就是直接覆盖住整个。
如果没有污渍,答案直接为0。
#include <bits/stdc++.h>
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define Mid ((l + r) / 2)
using namespace std;
int n, ans[51][51][51][51];
int g[59][59];
char c[59][59];
int get(int x, int y, int xx, int yy) {
if(x > xx || y > yy) return 0;
return g[xx][yy] - g[x - 1][yy] - g[xx][y - 1] + g[x - 1][y - 1];
}
int query(int x, int y, int xx, int yy) {
if(ans[x][y][xx][yy] != -1) return ans[x][y][xx][yy];
if(get(x, y, xx, yy) == 0) return ans[x][y][xx][yy] = 0;
int minncost = 0x3f3f3f3f;
for(int i = x; i + 1 <= xx; i++) {
minncost = min(minncost, query(x, y, i, yy) + query(i + 1, y, xx, yy));
}
for(int i = y; i + 1 <= yy; i++) {
minncost = min(minncost, query(x, y, xx, i) + query(x, i + 1, xx, yy));
}
if(xx - x == yy - y) {
minncost = min(minncost, xx - x + 1);
}
return ans[x][y][xx][yy] = minncost;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
memset(ans, -1, sizeof(ans));
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> (c[i] + 1);
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
g[i][j] = g[i - 1][j] + g[i][j - 1]- g[i - 1][j - 1] + (c[i][j] == '#');
}
}
cout << query(1, 1, n, n) << endl;
return 0;
}
Ex - Manhattan Christmas Tree
给定若干个点坐标,q个询问,每次询问一个点,到曼哈顿距离第k大的点的曼哈顿距离是多少。
借此题巩固一下知识点:曼哈顿距离转切比雪夫距离。
到一个点曼哈顿距离为定值的点集,是一个45度角的正方形。
到一个点切比雪夫距离为定值的点集,是一个平行坐标轴的正方形。
转变坐标系,原来为\((x, y)\)的点,变成\((x + y, x - y)\),则现在图上两点间的切比雪夫距离等于变换坐标系前这两点的曼哈顿距离。
比如\((1, 2)\)到\((5, 4)\)的曼哈顿距离为\(6\),\((3, -1)\)到\((9, 1)\)的切比雪夫距离就是6。
同理,逆操作,转变坐标系,原来为\((x, y)\)的点,变成\((\frac{x + y}{2}, \frac{x - y}{2})\),则现在图上两点间的曼哈顿距离等于变换坐标系前这两点的切比雪夫距离。
比如\((3, -1)\)到\((9, 1)\)的切比雪夫距离就是6,\((1, 2)\)到\((5, 4)\)的曼哈顿距离为\(6\)。
在这道题来说,我们可以二分第k大的点到目标点的距离,然后问题转化为如何求与目标点曼哈顿距离为\(mid\)的点的数量。
这个不太好做,\(x = x_a - mid\)的点有1个,\(x = x_a - mid + 1\)的点有3个...
但是,如果我们把坐标系的曼哈顿距离转化为切比雪夫距离,那么就是问转化完的图中,与目标点切比雪夫距离为\(mid\)的点的数量。
显然,这个区域是一个正方形,我们可以树状数组维护每一个y坐标小于等于y的点集合,存在一个升序的vector里面,这样每次可以二分出点的范围,搞出数量就行。
每个点最多存在logn个数组中,这样空间复杂度是\(nlogn\)的。
#include <bits/stdc++.h>
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define Mid ((l + r) / 2)
using namespace std;
const int N = 5e5 + 2;
int n;
vector<pair<int, int> > a;
vector<int> v[2 * N];
void add(int x, int y) {
x += N; y += N;
for( ; y < 2 * N; y += y & -y)
v[y].push_back(x);
}
int get(int y, int lx, int rx) {
y += N; lx += N; rx += N;
if(y <= 0) y = 1;
if(lx <= 0) lx = 1;
if(rx >= 2 * N) rx = 2 * N - 1;
int ans = 0;
for( ; y; y -= y & -y) {
ans += upper_bound(v[y].begin(), v[y].end(), rx) - lower_bound(v[y].begin(), v[y].end(), lx);
}
return ans;
}
int query(int lx, int ly, int rx, int ry) {
return get(ry, lx, rx) - get(ly - 1, lx, rx);
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
int x, y;
cin >> x >> y;
a.push_back({x + y, x - y});
}
sort(a.begin(), a.end());
for(auto p : a) {
add(p.first, p.second);
}
int q;
cin >> q;
while(q--) {
int a, b, k;
cin >> a >> b >> k;
int l = 0, r = 200009;
while(l <= r) {
if(query(a + b - Mid, a - b - Mid, a + b + Mid, a - b + Mid) < k) l = Mid + 1;
else r = Mid - 1;
}
cout << l << endl;
}
return 0;
}