ICPC 2019North-Western Russia (7/13)

ICPC 2019-2020 North-Western Russia Regional Contest

A - Accurate Movement

签到

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
int const MAXN = 2e5 + 10;
int n, m, T;

int main() {
    int a, b, c;
    cin >> a >> b >> c;
    if (c == b)
        cout << 1;
    else if ((c - b) % (b - a))
        cout << 2 * ((c - b) / (b - a)) + 3;
    else
        cout << 2 * ((c - b) / (b - a)) + 1;
    return 0;
}

B - Bad Treap

大意:

给出笛卡尔树的定义,现在要求给出 n 个点对 ( x , sin( x ) ),使得笛卡尔树的高度尽可能大

思路:

题意就是要找到一个长度为n的递增序列,且sin(ai)也是递增的

考虑到如果sinx极小,那么sin2x sin3x....一直到sinnx都是很小的,所以只需要找到一个最最最接近于0的sin值,然后输出这个点的倍数即可,因为不能超过2^32,所以向左偏1e9

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
struct node {
    double sin;
    int x;
} a[N];
bool cmp(node a, node b) { return a.sin < b.sin; }
int main() {
    int cnt = 0;
    for (int i = 1; i <= 1e5; i++) {
        if(sin(i)>0)
        a[cnt].x = i, a[cnt++].sin = sin(i);
    }
    sort(a, a + cnt, cmp);
    int n;
    cin >> n;
    for (int i = 0; i < n;i++){
        cout << (LL)a[0].x * i -100000000<< endl;
    } return 0;
}

E - Equidistant

大意:

给出n个点的树,然后上面有m个特殊点,要求求出一个点,使得这个点到所有的m个点的距离都相同

思路:

bfs,先将每个特殊点都入队,然后每次到一个数,都看是否有别的点先到达,只有同时到达或者第一次到达才把这个点入队,还要标记一下每个点同时被多少点到达,最后看是否存在一个点同时被m个特殊点到达即可

#include <bits/stdc++.h>

#define int long long
using namespace std;

inline int read() {
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

int const MAXN = 3e5 + 10, MAXM = MAXN * 3;
int n, m, T, vis[MAXN], dis[MAXN];
int idx, e[MAXM], h[MAXN], ne[MAXM], num[MAXN], inque[MAXN];
queue<int> q;

void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; }

signed main() {
    n = read(), m = read();
    for (int i = 1; i <= n + 10; ++i) h[i] = -1;
    for (int i = 1; i <= n - 1; ++i) {
        int a, b;
        a = read(), b = read();
        add(a, b), add(b, a);
    }
    for (int i = 1; i <= m; ++i) {
        int c;
        c = read();
        dis[c] = 0;
        num[c] = 1;
        vis[c] = 1;
        inque[c] = 1;
        q.push(c);
    }
    while (q.size()) {
        auto t = q.front();
        q.pop();
        inque[t] = 0;
        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if ((dis[j] && dis[j] != dis[t] + 1) || vis[j] == 1) {
                vis[j] = 1;
                continue;
            }
            dis[j] = dis[t] + 1;
            num[j] += num[t];
            if (!inque[j]) q.push(j), inque[j] = 1;
        }
    }
    int flg = 0, res = 0;
    for (int i = 1; i <= n; ++i) {
        if (num[i] == m) {
            flg = 1;
            res = i;
            break;
        }
    }
    if (!flg)
        printf("NO
");
    else {
        printf("YES
");
        printf("%d
", res);
    }
    return 0;
}

H - High Load Database

大意:

给出n(2e5)个数,以及q(1e5)个询问,每次询问给出一个数x,问将n个数最少分成多少个连续的段,才能使得每个段的和都小于等于x,不能就输出impossible,保证n个数的和不大于1e6

思路:

先求前缀和,然后对于每个询问二分去跳,每跳一次就是一个段,然后输出答案即可,需要注意的是,这个方法可能会因为x比较小,所以导致二分查询退化成了n的复杂度,那么整体复杂度最坏可能达到nq,但是这些x比较少,所以如果记忆化答案,就可以避免超时。可以先把1e6的答案都跑一遍存下来,也可以对于每次查询看之前是否查询过

#include <bits/stdc++.h>

#define int long long
using namespace std;

inline int read() {
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

int const N = 2e6 + 10;
int n, m, T, sum[N], a[N];
int res[N];
signed main() {
    n = read();
    for (int i = 1; i <= n; i++) a[i] = read(), sum[i] = sum[i - 1] + a[i];
    int q = read();
    memset(res, -1, sizeof res);
    for (int i = 0; i <= sum[n]; i++) {
        int x = i;
        int tmp = x;
        int start = 1;
        int flag = 1;
        int cnt = 0;
        while (start <= n) {
            int pos = upper_bound(sum + start, sum + 1 + n, tmp) - sum;
            if (pos == start) {
                flag = 0;
                break;
            }
            start = pos;
            tmp = x + sum[pos - 1];
            cnt++;
        }
        if (!flag)
            res[x] = -1;
        else {
            res[x] = cnt;
        }
    }
    while (q--) {
        int x = read();
        if (x > sum[n]) {
            cout << 1 << endl;
        } else {
            if (res[x] != -1) {
                cout << res[x] << endl;
            }
            else{
                cout << "Impossible" << endl;
            }
        }
    }
    return 0;
}

I - Ideal Pyramid

大意:

给定 N 个柱子的坐标和高度,要求找到一个最小的四棱锥(斜面和底面夹角为45°),使得全部的柱子都在里面

思路:

因为斜面和底面夹角为45度,所以h<=R-max(dx,dy)

这就将三维的问题转化为了2维的问题,分别考虑x和y,那么能够求出来金字塔底面的上下左右范围,然后取中心即可

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int n;
struct node {
    LL x, y, h;
} a[N];
LL INF = 0x3f3f3f3f3f3f3f;
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i].x >> a[i].y >> a[i].h;
    LL u = INF, d = -INF, l = INF, r = -INF;
    for (int i = 0; i < n; i++) {
        u = min(u, a[i].x - a[i].h);
        d = max(d, a[i].x + a[i].h);
        l = min(l, a[i].y - a[i].h);
        r = max(r, a[i].y + a[i].h);
    }
    LL x = (u + d) / 2, y = (l + r) / 2;
    LL h = (max(d - u, r - l) + 1) / 2;
    cout << x << ' ' << y << ' ' << h;
    return 0;
}

J - Just the Last Digit

大意:

给出一个n*n的矩阵,其中(a_{i,j})是i到j的路径个数再对10取模,现在保证每条路都从较小编号的点连到较大编号的点,问能否求出每个点之间是否有边

思路:

对10取模是个幌子,我们先看没有这个限制的时候怎么做

注意到两个点之间的路径只可能是:

1.直接到达

2.经过一个点间接到达

如果求出i和j之间间接到达的路径数量,看是否和输入相同,就知道两个点是否有路径了。

又因为保证了边都是从较小的点到较大的点,所以1号点和2号点是否有边是已知的,那么就可以一路推下去了。

再考虑取模这个条件,那么只需要在计算路径个数的时候对10取模即可

#include <bits/stdc++.h>

using namespace std;

const int N = 500 + 5;
typedef long long LL;
int n, mp[N][N], res[N][N];
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%1d", &mp[i][j]);
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            for (int k = i + 1; k < j; k++) {
                res[i][j] = (res[i][j] + res[i][k] * mp[k][j]) % 10;
            }
            if (res[i][j] == mp[i][j])
                res[i][j] = 0;
            else
                res[i][j] = 1;
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cout << res[i][j];
        }
        cout << endl;
    }
    return 0;
}

M - Managing Difficulties

大意:

给出n(2e3)个数,要求求出满足:

i < j < k且(a_j-a_i==a_k-a_j)的数对的数量

思路:

因为n比较小,所以可以想到直接枚举i和j,然后找他们和的一半出现的数量,一开始用的mulityset,一直T,因为还有t次询问,这样可能(O(n^2log(n)))的算法可能会卡爆,改成unordered_map就过了...

#include <bits/stdc++.h>

using namespace std;
#define int long long
int const MAXN = 2e3 + 10;
int n, m, T;
int a[MAXN];
unordered_map<int, int> mp;
signed main() {
    cin >> T;
    while (T--) {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        int ans = 0;
        for (int i = 1; i <= n - 2; i++) {
            mp.clear();
            mp[a[i + 1]]++;
            for (int k = i + 2; k <= n; k++) {
                if ((a[i] + a[k]) % 2) {
                    if (mp.count(a[k]))
                        mp[a[k]]++;
                    else
                        mp[a[k]] = 1;
                    continue;
                }
                int j = (a[i] + a[k]) / 2;
                if (mp.count(j)) ans += mp[j];
                if (mp.count(a[k]))
                    mp[a[k]]++;
                else
                    mp[a[k]] = 1;
            }
        }
        cout << ans << endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14492740.html