• 2020-04-23校内考试


    这次考试总的来说还算正常发挥,不过因为对算法不熟悉没有很快想出T2最后想出来却没时间调了。

    最后得分100+77+100=277,rank2。(不过没AK有点可惜

    刚开始我先把每到题看了一遍,然后T1没太理解,T2没啥思路,于是先去做T3。

    T3一看就是道主席树的裸题。

    维护一下值和出现次数就行了,我很快就打完了代码,但交上去只有25pts。

    当时心态有点炸,仔细检查一下发现是再减去lca的值时没有*2,改了之后轻松A掉了这一题。

    然后我再去做T2。

    T2想到一个很简单的单次查询$O(n)$的暴力贪心,写完交上去拿了77pts,比期望高27pts,估计是出题人用脚造数据吧。

    然后就没有在去看T2了,决定先去搞T1。

    此时刚刚过去1/3的时间。

    T1描述太过奇怪,我大概又花了20min才看懂,感觉是个贪心。

    然后写了个代码交上去只有25pts。写个对拍(这花了我很长时间)发现了一些错误,改了之后交上去就A了。

    这是离结束只有30min。

    A了这题之后我信心大涨,去看T2。

    我突然想到T2说不定可以用倍增去优化之前的暴力(赛后发现我的想法是对的),写了之后有点小问题。

    这时离考试结束只有2min了,最后也没改过哪个错误,考试结束才改过来。

    赛后发现做出来的人最多的T2我却没做出来,我认为这主要原因是我对某些算法的不熟悉,毕竟我很久没练倍增了。

    还有就是花了太久时间取读T1的题意了。

    接下来我就来讲一下题解:

    T1 游戏公司

    原题:AGC004D

    题目描述:

    题解:

    很容易想到贪心。

    首先这个题很毒瘤的是1号节点必须连一条到自己的自环。剩下的结构如果我们把每条边取反可以得到一棵以1为根的树。

    然后处理每个节点的深度和可以到达的最大深度,然后从叶节点向上回溯的时候如果某一个节点这个数的差>=k的时候,就得把它连向1,之后它的子树到1的距离都小于等于k了,如果它的父亲即是一号点,则如果这个差值>=k-1时都要连向1.

    如果它连向1了一条边,则它的父亲统计最大深度时也不用统计它的子树了。

    注意如果1本来没连向自己答案还要加1。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    const int N = 100010;
    struct node{
        int pre, to;
    }edge[N << 1];
    int head[N], tot;
    int n, k;
    int a[N];
    int dis[N], mx[N];
    int ans;
    bool vis[N];
    void dfs(int x) {
        for (int i = head[x]; i; i = edge[i].pre) {
            int y = edge[i].to;
            dis[y] = dis[x] + 1;
            dfs(y);
            mx[x] = max(mx[x], mx[y]);
        }
        mx[x] = max(mx[x], dis[x]);
        if (x != 1) {
            if (mx[x] - dis[x] >= k) mx[x] = 0, ans++;
            if (mx[x] - dis[x] == k - 1 && a[x] != 1) mx[x] = 0, ans++;
        }
    }
    void add(int u, int v) {
        edge[++tot] = node{head[u], v};
        head[u] = tot;
    }
    int main() {
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            if (i == 1) {
                if (a[i] != 1) ans++;
            } else {
                add(a[i], i);
            }
        }
        dfs(1);
        cout << ans;
        return 0;
    }

    T2 偷渡者xibo

    原题:ARC060C

    题目描述

     题解

    考虑暴力贪心,其实就是求它一天最远能走到哪,而这样做的复杂度是$O(n)$的显然不行(有77pts)。

    考虑优化,如果二分找到一天最远能走到哪,复杂度数$O(ans imes log{n})$的,最坏情况还不如上一个暴力(亲测只有66pts)。

    考虑每次不止走一天,很容易想到倍增优化:dp[i][j]代表从i走$2^j$天最远能走多远,有dp[i][j]=dp[dp[i][j-1]][j-1]

    然后就很简单了。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N = 1000010;
    int n;
    int a[N];
    int L, q, ans, lst;
    int dp[N][21];
    int pow[21];
    int main() {
        memset(dp, 0x3f, sizeof(dp));
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        scanf("%d", &L);
        scanf("%d", &q);
        pow[0] = 1;
        for (int i = 1; i <= n; i++) {
            pow[i] = pow[i - 1] << 1;
            lst = max(i, lst);
            for (int j = lst + 1; j <= n; j++) {
                if (a[j] - a[i] <= L) {
                    lst = j;
                }
            }
            dp[i][0] = lst;
        }
        for (int j = 1; j <= 20; j++) {
            for (int i = 1; i <= n; i++) {
                if (dp[i][j - 1] < 0x3f3f3f3f) dp[i][j] = dp[dp[i][j - 1]][j - 1];
            }
        }
        while (q--) {
            int x, y;
            scanf("%d%d", &x, &y);
            if (x > y) swap(x, y);
            ans = 0;
            for (int i = 20; i >= 0; i--) {
                if (dp[x][i] < y) {
                    x = dp[x][i];
                    ans += pow[i];
                }
            }
            printf("%d
    ", ans + 1);
        }
        return 0;
    }

    T3 五彩树

    原题:ABC133F

    主席树裸题,不讲了。

    #include <iostream>
    #include <cstdio>
    using namespace std;
    typedef long long ll;
    const ll N = 100010;
    struct Segment{
        ll cnt, val, lson, rson;//出现次数,值 
    }tr[N * 20];
    ll rt[N];
    struct node{
        ll pre, to, col, val;
    }edge[N << 1];
    ll head[N], tot;
    ll n, q;
    ll dis[N], dep[N], fa[N], son[N], sz[N], bl[N];
    void dfs1(ll x) {
        sz[x] = 1;
        for (ll i = head[x]; i; i = edge[i].pre) {
            ll y = edge[i].to;
            if (y == fa[x]) continue;
            dep[y] = dep[x] + 1;
            dis[y] = dis[x] + edge[i].val;
            fa[y] = x;
            dfs1(y);
            sz[x] += sz[y];
            if (sz[y] > sz[son[x]]) {
                son[x] = y;
            }
        }
    }
    void dfs2(ll x, ll chain) {
        bl[x] = chain;
        if (son[x]) dfs2(son[x], chain);
        for (ll i = head[x]; i; i = edge[i].pre) {
            ll y = edge[i].to;
            if (y == fa[x] || y == son[x]) continue;
            dfs2(y, y);
        }
    }
    ll LCA(ll u, ll v) {
        while (bl[u] != bl[v]) {
            if (dep[bl[u]] < dep[bl[v]]) swap(u, v);
            u = fa[bl[u]];
        }
        return dep[u] < dep[v] ? u : v;
    }
    void push_up(ll cur) {
        tr[cur].cnt = tr[tr[cur].lson].cnt + tr[tr[cur].rson].cnt;
        tr[cur].val = tr[tr[cur].lson].val + tr[tr[cur].rson].val;
    }
    void ins(ll &cur, ll pre, ll l, ll r, ll pos, ll v) {
        cur = ++tot;
        tr[cur] = tr[pre];
        if (l == r) {
            tr[cur].cnt++;
            tr[cur].val += v;
            return;
        }
        ll mid = (l + r) >> 1;
        if (pos <= mid) ins(tr[cur].lson, tr[pre].lson, l, mid, pos, v);
        else ins(tr[cur].rson, tr[pre].rson, mid + 1, r, pos, v);
        push_up(cur);
    }
    ll ask_val(ll cur, ll l, ll r, ll pos) {
        if (l == r) return tr[cur].val;
        ll mid = (l + r) >> 1;
        if (pos <= mid) return ask_val(tr[cur].lson, l, mid, pos);
        else return ask_val(tr[cur].rson, mid + 1, r, pos); 
    }
    ll ask_cnt(ll cur, ll l, ll r, ll pos) {
        if (l == r) return tr[cur].cnt;
        ll mid = (l + r) >> 1;
        if (pos <= mid) return ask_cnt(tr[cur].lson, l, mid, pos);
        else return ask_cnt(tr[cur].rson, mid + 1, r, pos); 
    }
    void solve(ll x) {
        for (ll i = head[x]; i; i = edge[i].pre) {
            ll y = edge[i].to;
            if (y == fa[x]) continue;
            ins(rt[y], rt[x], 1, n, edge[i].col, edge[i].val);
            solve(y);
        }
    }
    void add(ll u, ll v, ll c, ll d) {
        edge[++tot] = node{head[u], v, c, d};
        head[u] = tot;
    }
    int main() {
        scanf("%lld%lld", &n, &q);
        for (ll i = 1, u, v, c, d; i < n; i++) {
            scanf("%lld%lld%lld%lld", &u, &v, &c, &d);
            add(u, v, c, d);
            add(v, u, c, d);
        }
        dfs1(1);
        dfs2(1, 1);
        solve(1);
        while (q--) {
            ll x, y, u, v;
            scanf("%lld%lld%lld%lld", &x, &y, &u, &v);
            ll lca = LCA(u, v);
            ll value = ask_val(rt[u], 1, n, x) + ask_val(rt[v], 1, n, x) - 2 * ask_val(rt[lca], 1, n, x);
            ll cnt = ask_cnt(rt[u], 1, n, x) + ask_cnt(rt[v], 1, n, x) - 2 * ask_cnt(rt[lca], 1, n, x);
            printf("%lld
    ", dis[u] + dis[v] - 2 * dis[lca] - value + cnt * y);
        }
        return 0;
    }
  • 相关阅读:
    Linux设备管理(一):kobject, kset, ktype分析
    Keepalived高可用集群
    Nginx反向代理与负载均衡
    LNMP环境应用实践
    HTTP协议
    Nginx详解
    MySQL进阶
    MySQL安装与基本使用
    Jumpserver跳板机
    PXE自动化装机
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/12768069.html
Copyright © 2020-2023  润新知