第四场选拔赛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chenzhenyu123456/article/details/69922997

A
思路:其实这里用了树的直径的性质:把一棵树砍两半,最远点对一定是这两棵新树里面的最远点对之间的一组组合。
这样预处理 L C A ,然后线段树维护即可。
参考代码:

#include <bits/stdc++.h>
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
struct Edge {
    int v, w, nt;
    Edge() {}
    Edge(int b, int c, int d) : v(b), w(c), nt(d) {}
};
Edge edge[MAXN << 1];
int head[MAXN], edgenum;
int vs[MAXN << 1], depth[MAXN << 1];
int id[MAXN];
int dist[MAXN];
int dfs_clock;
void Init() { edgenum = 0; memset(head, -1, sizeof(head)); }
void addEdge(int u, int v, int w) {
    edge[edgenum] = {v, w, head[u]};
    head[u] = edgenum++;
}
void DFS(int u, int fa, int d) {
    id[u] = dfs_clock;
    vs[dfs_clock] = u;
    depth[dfs_clock++] = d;
    for(int i = head[u]; i != -1; i = edge[i].nt) {
        int v = edge[i].v;
        if(v == fa) continue;
        dist[v] = dist[u] + edge[i].w;
        DFS(v, u, d + 1);
        vs[dfs_clock] = u;
        depth[dfs_clock++] = d;
    }
}
void find_depth() {
    dfs_clock = 1;
    memset(vs, 0, sizeof(vs));
    memset(depth, 0, sizeof(depth));
    memset(id, 0, sizeof(id));
    memset(dist, 0, sizeof(dist));
    DFS(1, -1, 0);
}
int dp[MAXN << 1][30];
void RMQ_init(int N) {
    for(int i = 1; i <= N; i++) dp[i][0] = i;
    for(int j = 1; (1 << j) <= N; j++) {
        for(int i = 1; i + (1 << j) - 1 <= N; i++) {
            int a = dp[i][j - 1];
            int b = dp[i + (1 << (j-1))][j-1];
            dp[i][j] = depth[a] < depth[b] ? a : b;
        }
    }
}
int query(int L, int R) {
    int k = 0;
    while(1 << (k + 1) <= R - L + 1) k++;
    int a = dp[L][k];
    int b = dp[R - (1 << k) + 1][k];
    return depth[a] < depth[b] ? a : b;
}
int LCA(int u, int v) {
    int x = id[u];
    int y = id[v];
    return x < y ? vs[query(x, y)] : vs[query(y, x)];
}
int Query_Dis(int u, int v) {
    return dist[u] + dist[v] - 2 * dist[LCA(u, v)];
}
struct Tree {
    int l, r;
    pii val;
    Tree() {}
    Tree(int x, int y, pii z) : l(x), r(y), val(z) {}
};
Tree tree[MAXN << 2];
void gmax(int x, int &y) {
    y = max(x, y);
}
pii Work(pii x, pii y) {
    int ans1 = Query_Dis(x.first, y.first);
    int ans2 = Query_Dis(x.first, y.second);
    int ans3 = Query_Dis(x.second, y.first);
    int ans4 = Query_Dis(x.second, y.second);
    int ans5 = Query_Dis(x.first, x.second);
    int ans6 = Query_Dis(y.first, y.second);
    int ans = 0;
    gmax(ans1, ans);
    gmax(ans2, ans);
    gmax(ans3, ans);
    gmax(ans4, ans);
    gmax(ans5, ans);
    gmax(ans6, ans);
    if(ans == ans1) {
        return pii(x.first, y.first);
    }
    else if(ans == ans2) {
        return pii(x.first, y.second);
    }
    else if(ans == ans3) {
        return pii(x.second, y.first);
    }
    else if(ans == ans4) {
        return pii(x.second, y.second);
    }
    else if(ans == ans5) {
        return x;
    }
    else {
        return y;
    }
}
void Build(int l, int r, int o) {
    tree[o] = {l, r, pii(0, 0)};
    if(l == r) {
        tree[o].val = pii(l, l);
        return ;
    }
    int mid = l + r >> 1;
    Build(l, mid, ll);
    Build(mid + 1, r, rr);
    tree[o].val = Work(tree[ll].val, tree[rr].val);
}
pii Query(int L, int R, int o) {
    if(tree[o].l == L && tree[o].r == R) {
        return tree[o].val;
    }
    int mid = tree[o].l + tree[o].r >> 1;
    if(R <= mid) {
        return Query(L, R, ll);
    }
    else if(L > mid) {
        return Query(L, R, rr);
    }
    else {
        return Work(Query(L, mid, ll), Query(mid + 1, R, rr));
    }
}
int main()
{
    int n, q;
    while(scanf("%d%d", &n, &q) != EOF) {
        Init();
        for(int i = 1; i <= n - 1; i++) {
            int u, v, w; scanf("%d%d%d", &u, &v, &w);
            addEdge(u, v, w);
            addEdge(v, u, w);
        }
        find_depth(); RMQ_init(dfs_clock - 1);
        Build(1, n, 1);
        while(q--) {
            int l, r; scanf("%d%d", &l, &r);
            pii ans = Query(l, r, 1);
            printf("%d\n", Query_Dis(ans.first, ans.second));
        }
    }
    return 0;
}

B
解法一:贪心模拟即可。
解法二: d p [ i ] [ 0 ] 表示前 i 棵且不选第 i 棵的贡献, d p [ i ] [ 1 ] 表示前 i 棵必选第 i 棵的贡献

C
思路:找到不匹配括号的位置 p o s ,左(右)括号的贡献是 | s t r | p o s ( p o s + 1 )

D
思路:选 1 , 2 , 4 , 8 , . . . , 2 n

E
思路:注意到只有 15 个点,先预处理每个点的最短路,之后就是状压 d p 了。
d p [ i ] [ j ] 表示状态为 i 且最后选择 j 的最优贡献。

F
思路:一个数最多 30 次更新就为 0 了,直接用线段树维护即可,在每个区间打个标记 f l a g ,看看是否全为 0

G
思路:找循环节或者按位统计,用快速幂可以很方便的统计。
这里贴个不用快速幂的程序。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10;
typedef long long LL;
int val[11][4] = {
    0, 0, 0, 0,
    1, 1, 1, 1,
    2, 4, 8, 6,
    3, 9, 7, 1,
    4, 6, 4, 6,
    5, 5, 5, 5,
    6, 6, 6, 6,
    7, 9, 3, 1,
    8, 4, 2, 6,
    9, 1, 9, 1,
    0, 0, 0, 0
};
int main()
{
    int t; scanf("%d", &t);
    while(t--) {
        int n, k; scanf("%d%d", &n, &k);
        int ans = 0;
        if(k % 4 == 0) {
            k = 3;
        }
        else {
            k = k % 4 - 1;
        }
        for(int i = 1; i <= 10; i++) {
            ans += val[i][k];
            ans %= 10;
        }
        ans *= n / 10; ans %= 10;
        n %= 10;
        for(int i = 1; i <= n; i++) {
            ans += val[i][k];
            ans %= 10;
        }
        printf("%d\n", ans);
    }
    return 0;
}

H
签到

I
思路:用类似食物链并查集来做,不过你直接 c h e c k 也是可行的吧,只是坑点较多。

J
思路:对第四种操作,打个标记即可。其他就很随意了,链表随便搞。

猜你喜欢

转载自blog.csdn.net/chenzhenyu123456/article/details/69922997