概述
比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)
训练网址
A. Intelligent Warehouse
- 题意:求给定集合的偏序集的最长链,偏序关系为整除。
- 动态规划。其实就是第一层枚举 a i a_i ai,第二层枚举倍数。但是这个题卡常好严重。
- 忘掉了忘掉了,用素数优化呀。只需要找倍数为素数就可以了。
- 有一个地方非常需要小心,就是如果用素数的话,那么就不可以加上
if(!cnt[i]) continue;
. 因为枚举素数的话,比如x的4倍就枚举不到了,那么需要两次2倍才可以传过去。这个时候 f ( 2 ∗ i ) f(2*i) f(2∗i) 就有用了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10000010;
int f[maxn], cnt[maxn];
int prime[maxn], sz;
bool st[maxn];
int N;
void sieve(int N)
{
for(int i = 2; i <= N; i++){
if(!st[i]) prime[sz++] = i;
for(int j = 0; prime[j] <= N / i; j++){
st[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}
}
}
int main()
{
sieve(maxn - 1);
scanf("%d", &N);
int x;
for(int i = 0; i < N; i++){
scanf("%d", &x);
++cnt[x];
++f[x];
}
int ans = 0;
for(int i = 1; i <= 10000000; i++){
//if(!cnt[i]) continue;
for(int j = 0; j < sz && (ll)prime[j] * i <= 10000000; j++){
int p = prime[j];
f[p * i] = max(f[i * p], f[i] + cnt[i * p]);
}
ans = max(ans, f[i]);
}
printf("%dn", ans);
return 0;
}
B. Intelligent Robot
- 二维平面,给起点和终点,平面上有一些障碍,一个机器人从起点到终点需要走的最短距离是多少?可以接触障碍的端点也可以沿着障碍走,但是不能跨过障碍。
- 这个题和2020南京站的那个滑雪的题目很相似,都是沿着端点走,一定是最小值。那么可以暴力判断点两两之间有没有障碍。然后建图后跑一个dijkstra即可。
- dijkstra 边权即使是浮点数,即使边权有0,也不会超时。
- 数组开小会引起超时!
- 函数无返回值会引起非常莫名其妙的错误,比如加一个printf之后就会段错误(一部分是编译优化的引起的)
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int maxn = 610, maxm = 400000;
typedef pair<double, double> P;
const double eps = 1e-8, INF = 1e18;
int sign(double x)
{
if(fabs(x) < eps) return 0;
if(x < 0) return -1;
return 1;
}
P operator+(P a, P b)
{
return {a.x + b.x, a.y + b.y};
}
P operator-(P a, P b)
{
return {a.x - b.x, a.y - b.y};
}
double operator *(P a, P b)
{
return a.x * b.x + a.y * b.y;
}
double cross(P a, P b)
{
return a.x * b.y - a.y * b.x;
}
bool intersection(P a1, P a2, P b1, P b2)
{
double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1);
double c3 = cross(b2 - b1, a2 - b1), c4 = cross(b2 - b1, a1 - b1);
//下面不可以写成 <=
return sign(c1) * sign(c2) < 0 && sign(c3) * sign(c4) < 0;
}
double dist(P a, P b)
{
//这个地方把 double 写成 return 了,函数无返回值导致访问了非法内存。
double dx = a.x - b.x, dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
int h[maxn], e[maxm], ne[maxm], idx;
double w[maxm];
int maxx, maxy, N;
P st[maxn], ed[maxn], start, goal, points[maxn];
int sz;
void add(int a, int b, double c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
bool vis[maxn];
double d[maxn];
double dijkstra(int s, int t, int n)
{
typedef pair<double, int> PII;
for(int i = 1; i <= n; i++) d[i] = INF;
priority_queue<PII, vector<PII>, greater<PII>> que;
d[s] = 0;
que.push({0, s});
while(que.size()){
auto p = que.top(); que.pop();
int u = p.y;
if(vis[u]) continue;
vis[u] = true;
for(int i = h[u]; i != -1; i = ne[i]){
int v = e[i];
if(d[v] > d[u] + w[i]){
d[v] = d[u] + w[i];
que.push({d[v], v});
}
}
}
return d[t];
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d%d%d", &maxx, &maxy, &N);
for(int i = 1; i <= N; i++){
scanf("%lf%lf%lf%lf", &st[i].x, &st[i].y, &ed[i].x, &ed[i].y);
points[++sz] = st[i]; points[++sz] = ed[i];
}
scanf("%lf%lf%lf%lf", &start.x, &start.y, &goal.x, &goal.y);
points[++sz] = start;
points[++sz] = goal;
// for(int i = 1; i <= sz; i++){
// printf("*** %f %fn", points[i].x, points[i].y);
// }
for(int i = 1; i <= sz; i++){
for(int j = i + 1; j <= sz; j++){
bool flag = true;
for(int k = 1; k <= N && flag; k++){
if(intersection(points[i], points[j], st[k], ed[k])) {
flag = false;
}
}
if(flag) {
double dd = dist(points[i], points[j]);
//printf("%d %d %fn", i, j, dd);
add(i, j, dd), add(j, i, dd);
}
}
}
printf("%.4fn", dijkstra(sz - 1, sz, sz));
return 0;
}
D. Router Mesh
E. Phone Network
- 题意:一个长度n的数组,每个数在[ 1,m ]区间内,保证了每个数(在[ 1 , m ])至少出现过一次。对于每个 i ∈ [ 1 , m ],分别输出在原数列中,包含了 [ 1 , i ] 的所有种类数字的最短区间长度。
- 别人的题解
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200010, INF = 1e9;
struct node
{
//mi[i] 表示从i开始,包含 1~x的最短区间,的右端点。
int l, r;
int mi, len, lazy;
}tr[maxn * 4];
int N, M;
vector<int> w[maxn];
void pushup(int u)
{
tr[u].mi = min(tr[2 * u].mi, tr[2 * u + 1].mi);
tr[u].len = min(tr[2 * u].len, tr[2 * u + 1].len);
}
void pushdown(int u)
{
int t = tr[u].lazy;
if(t){
tr[2 * u].mi = tr[2 * u].lazy = t, tr[2 * u].len = t - tr[2 * u].r + 1;
tr[2 * u + 1].mi = tr[2 * u + 1].lazy = t, tr[2 * u + 1].len = t - tr[2 * u + 1].r + 1;
tr[u].lazy = 0;
}
}
void build(int u, int l, int r)
{
tr[u].l = l, tr[u].r = r, tr[u].len = INF;
tr[u].mi = tr[u].lazy = 0;
if(l == r) return;
int mid = (l + r) / 2;
build(2 * u, l, mid), build(2 * u + 1, mid + 1, r);
}
void modify(int u, int l, int r, int c)
{
//if(tr[u].r < l || tr[u].l > r) return;
if(l <= tr[u].l && tr[u].r <= r){
tr[u].mi = tr[u].lazy = c;
tr[u].len = c - tr[u].r + 1;
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
if(l <= mid) modify(2 * u, l, r, c);
if(r > mid) modify(2 * u + 1, l, r, c);
pushup(u);
}
//找到 [l, r] 中小于 last 的最大位置。
//即 如果mi[k]<p(i),表示x+1未被包含在这个区间,所以右端点要移到p(i),即mi[k]=p(i)
//因为last - mi[k] + 1 作为以 i 为左端点len,只能作为在 [pre, cnt] 的答案。
int find(int u, int l, int r, int last)
{
if(tr[u].mi >= last) return -1;
if(tr[u].l == tr[u].r) return tr[u].l;
pushdown(u);
int mid = (tr[u].l + tr[u].r) / 2;
int tmp = -1;
tmp = find(2 * u + 1, l, r, last);
if(tmp == -1 && l <= mid) tmp = find(2 * u, l, r, last);
return tmp;
}
int main()
{
scanf("%d%d", &N, &M);
for(int i = 1, x; i <= N; i++){
scanf("%d", &x);
w[x].push_back(i);
}
build(1, 1, N);
for(int i = 1; i <= M; i++){
int pre = 1, sz = w[i].size();
for(int j = 0; j < sz; j++){
int last = w[i][j];
int cnt = find(1, pre, last, last);
if(cnt != -1) modify(1, pre, cnt, last);
pre = last + 1;
}
if(w[i].back() + 1 <= N) modify(1, w[i].back() + 1, N, INF);
printf("%d ", tr[1].len);
}
return 0;
}
F. Design Problemset
-
题意:
-
思路:我们发现,假如我们确定了制作的 problemset的 数量,就可以很简单的判断出此方案是否可行。我们二分答案。首先把左端点加起来,大于 R的话方案数显然是0,右端点加起来小于 L 方案数显然是0.
-
二分的check具体来讲,我们首先看都取左区间的话,每类题目能否不超过最大的题目数量 a [ i ] a[i] a[i] 限制(超过返回false);然后我们看都取左端点能否大于等于 L(大于等于就返回 true);最后我们这样子取,假设要制作 x 个 problemset,我们取 min { x ∗ r [ i ] , a [ i ] } min{x * r[i], a[i]} min{x∗r[i],a[i]},比较是否能够达到 L.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
typedef long long ll;
ll L, R, N, l[maxn], r[maxn], a[maxn];
ll suml, sumr, sumr_l;
bool check(ll x)
{
for(int i = 1; i <= N; i++) if((__int128)x * l[i] > a[i]) return false;
if(suml >= L) return true;
__int128 res = 0;
for(int i = 1; i <= N; i++){
res += min((__int128)x * r[i], (__int128)a[i]);
}
return res >= (__int128)x * L;
}
int main()
{
scanf("%lld%lld%lld", &N, &L, &R);
for(int i = 1; i <= N; i++){
scanf("%lld", &a[i]);
}
for(int i = 1; i <= N; i++){
scanf("%lld%lld", &l[i], &r[i]);
suml += l[i], sumr += r[i], sumr_l += r[i] - l[i];
}
if(suml > R || sumr < L)
{
printf("0n");
}
else{
ll lb = 0, ub = 1e18;
while(ub > lb){
ll mid = (lb + ub + 1) / 2;
//printf("%lldn", mid);
if(check(mid)) lb = mid;
else ub = mid - 1;
}
printf("%lldn", lb);
}
return 0;
}
G. Tree Projection
I. Walking Machine
- 题意:一个地图上标着一些字母,表示在这个点要往哪个地方走。问有多少个点会走到地图外面。
- 很简单的题,深搜就行。走到地图外面就记为0,走到一个地图里面的环上就记为1. 初始化为-1表示尚未搜到。
- 为了方便开一个 vis 数组表示是否搜到,以及 f 数组表示这个点是什么类型的(走动地图外面还是走不到地图外面)。
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int, int>P;
unordered_map<char, P> dir;
const int maxn = 1010;
char mz[maxn][maxn];
bool vis[maxn][maxn];
int f[maxn][maxn];
int N, M;
int dfs(int x, int y)
{
vis[x][y] = true;
auto p = dir[mz[x][y]];
int nx = x + p.x, ny = y + p.y;
if (nx < 1 || nx > N || ny < 1 || ny > M) return f[x][y] = 0;
if(vis[nx][ny] && f[nx][ny] == -1) return f[x][y] = 1;
else if(vis[nx][ny]) return f[x][y] = f[nx][ny];
return f[x][y] = dfs(nx, ny);
}
int main()
{
memset(f, -1, sizeof f);
dir['W'] = {-1, 0}, dir['S'] = {1, 0}, dir['A'] = {0, -1}, dir['D'] = {0, 1};
scanf("%d%d", &N, &M);
for(int i = 1; i <= N; i++){
scanf("%s", mz[i] + 1);
}
for(int i = 1; i <= N; i++){
for(int j = 1; j <= M; j++){
if(!vis[i][j]) f[i][j] = dfs(i, j);
}
}
int ans = 0;
for(int i = 1; i <= N; i++){
for(int j = 1; j <= M; j++){
ans += (f[i][j] == 0);
}
}
printf("%dn", ans);
return 0;
}
J. Matrix Subtraction
- 题意:给一个 n × m ntimes m n×m 的矩阵。可以选中一个 a × b atimes b a×b 的子矩阵,把里面的所有数都减1(子矩阵是连续的一部分,不要记错),使得最后子矩阵的所有数都是0.
- 一个矩阵全为零,等价于他的二维差分矩阵全为0. 因为要想使左上角元素(比如第一行第一列的元素)变成0,只能把它作为小矩形的左上角做区间减法。因此我们遍历二分差分矩阵,如果出现大于0的元素,就以它作为小矩阵的左上角,对小矩阵做区间减法。最后看看二维差分数组是否全部变成了0.
- 有一个需要注意的地方是,遍离二维差分时,因为题目中要求只能减不能加,因此只能出现大于0的时候才减去,小于等于0的时候不可以。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
typedef long long ll;
ll m[maxn][maxn];
int N, M, A, B;
void insert(int x1, int y1, int x2, int y2, ll c)
{
m[x1][y1] += c;
m[x2 + 1][y1] -= c;
m[x1][y2 + 1] -= c;
m[x2 + 1][y2 + 1] += c;
}
int main()
{
int T;
scanf("%d", &T);
while(T--){
scanf("%d%d%d%d", &N, &M, &A, &B);
for(int i = 1; i <= N; i++){
fill(m[i], m[i] + M + 1, 0);
}
for(int i = 1; i <= N; i++){
for(int j = 1; j <= M; j++){
int x;
scanf("%d", &x);
insert(i, j, i, j, x);
}
}
for(int i = 1; i + A - 1 <= N; i++){
for(int j = 1; j + B - 1 <= M; j++){
if(m[i][j] > 0) insert(i, j, i + A - 1, j + B - 1, -m[i][j]);
}
}
bool flag = true;
for(int i = 1; i <= N && flag; i++){
for(int j = 1; j <= M && flag; j++){
if(m[i][j]) flag = false;
}
}
if(flag) printf("^_^n");
else printf("QAQn");
}
return 0;
}
最后
以上就是寒冷白昼为你收集整理的比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)的全部内容,希望文章能够帮你解决比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复