[hgoi#2019/3/10]赛后总结

时间:2023-03-08 22:17:30

关于本次hg模拟赛,题目来源于CF1110。

t1-无意义运算符(meaning)

题目描述

最大公约数和位运算之间有共同点吗?是时候来研究一下了。
给定一个正整数a,请找到一个闭区间[1,a-1] 内的某个整数b,使得a xor b 和a&b 的
最大公约数最大。换句话说,你要求出下面的函数:
\[f(a)=max(gcd(a \ xor \ b, a \ and \ b))(0<b<a)\]
其中\(xor\)表示按位异或,\(and\)表示按位与。

解法

笨蛋chh没有想到正解,就打表发现了规律。

ac代码

看看我冗长的垃圾代码。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ms(a, b) memset(a, b, sizeof(a))
#define LL long long
using namespace std;
inline LL gll() {
    int w = 0, x = 0;
    char ch = 0;
    while (!isdigit(ch)) w |= ch == '-', ch = getchar();
    while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return w ? -x : x;
}
LL n;
LL gcd(LL n, LL m) {
    return (m == 0) ?(n):(gcd(m, n % m));
}
LL solve(LL x) {
    if (x == 3) return 1;
    if (x == 7) return 1;
    if (x == 15) return 5;
    if (x == 31) return 1;
    if (x == 63) return 21;
    if (x == 127) return 1;
    if (x == 255) return 85;
    if (x == 511) return 73;
    if (x == 1023) return 341;
    if (x == 2047) return 89;
    if (x == 4095) return 1365;
    if (x == 8191) return 1;
    if (x == 16383) return 5461;
    if (x == 32767) return 4681;
    if (x == 65535) return 21845;
    if (x == 131071) return 1;
    if (x == 262143) return 87381;
    if (x == 524287) return 1;
    if (x == 1048575) return 349525;
    if (x == 2097151) return 299593;
    if (x == 4194303) return 1398101;
    if (x == 8388607) return 178481;
    if (x == 16777215) return 5592405;
    if (x == 33554431) return 1082401;
    if (x == 67108863) return 22369621;
    return 1;
}
int main() {
    freopen("meaning.in","r",stdin);
    freopen("meaning.out","w",stdout);
    int cas = gll();
    for (int _t = 1; _t <= cas; _t ++) {
        n = gll();
        LL ans = 1;
        while (ans - 1 < n) ans *= 2; ans -= 1;
        if (ans == n) printf("%lld\n",solve(n));
        else printf("%lld\n", ans);
    }
    return 0;
}

t2-麻将(jongmah)

题目描述

考虑一个变形的麻将游戏:你手头有一副包含n 个牌的麻将牌,每个牌上标有1 到m
的整数。
为了赢得麻将游戏,你必须将所有牌都组成一幅幅的“三张牌”,每一幅“三张牌”的
面值必须是连续的或相等的。如7; 7; 7 和12; 13; 14 都是一幅有效的“三张牌”,而2; 2; 3 和
2; 4; 6 都是无效的。
你想知道,对你手中的这幅麻将牌而言,离胡牌还有多远?也即,对于你手中的牌而言,
最多能凑出多少副“三张牌”?

解法

考试的时候,dp乱搞40分,我隔壁的慕容宝宝大佬模拟还是比我高,自闭ing。

ac代码

#include <bits/stdc++.h>
#define ms(a, b) memset(a, b, sizeof(a))
#define LL long long
using namespace std;
inline int gi() {
    int w = 0, x = 0;
    char ch = 0;
    while (!isdigit(ch)) w |= ch == '-', ch = getchar();
    while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return w ? -x : x;
}
#define N 1000005
int n, m;
int f[N][5][5], sum[N];
int main() {
    n = gi(), m = gi();
    ms(f, -1);
    f[0][0][0] = 0;
    for (int i = 1; i <= n; i ++) sum[gi()] ++;
    for (int i = 1; i <= m; i ++) {
        for (int j = 0; j < 3 ; j ++) {
            for (int k = 0 ; k <3 ; k ++) {
                for (int l = 0; l < 3; l ++)
                    if (sum[i] < j + k + l) continue;
                    else f[i][k][l] = max(f[i][k][l], f[i - 1][j][k] + (sum[i] - j - k - l) / 3 + l);
            }
        }
    }
    printf("%d\n",f[m][0][0]);
    return 0;
}

t3-魔法石(magic)

题目描述

Grigory 有n 块魔法石,连续编号成1 到n,第i 块魔法石的魔法能量是ci。
有一天Grigory 很无聊,他选择了一块内部(所谓内部就是指编号是2..n-1的)魔
法石,然后对和它相邻的魔法石进行了同步操作。同步后,那块魔法石就丢失了自己的魔法
能量,同时也获得了相邻魔法石的能量。也就是说,如果选中的魔法石的能量是ci,同步后
就变成了c'[i]=c[i+1]+c[i-1]-c[i] .
Grigory 的好朋友Andrew 也有n 块魔法石,他第i 块魔法石的魔法能量是ti。Grigory
很好奇得想知道他的这些魔法石是否可以通过零次或多次同步操作将其魔法能量转换成
Andrew 的对应的魔法石的能量值?也就是说,对于任意的i,是否可以将ci 变成ti?

分析

考试乱搞满分,吐槽数据太水,正解是差分数组排序。

ac代码

#include <bits/stdc++.h>
#define ms(a, b) memset(a, b, sizeof(a))
#define LL long long
using namespace std;
inline int gi() {
    int w = 0, x = 0;
    char ch = 0;
    while (!isdigit(ch)) w |= ch == '-', ch = getchar();
    while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return w ? -x : x;
}
#define N 100005
int n;
int a[N], b[N], c[N], d[N];
int main() {
    n = gi();
    for (int i = 1; i <= n; i ++) a[i] = gi();
    for (int i = 1; i <= n; i ++) b[i] = gi();
    if (a[1]!=b[1]||a[n]!=b[n]) {
        printf("No\n");
        return 0;
    }
    for (int i = 2; i <= n; i ++) {
        c[i - 1] = a[i] - a[i - 1];
        d[i - 1] = b[i] - b[i - 1];
    }
    sort(c + 1, c + n);
    sort(d + 1, d + n);
    for (int i = 1; i < n; i ++) {
        if (c[i] != d[i]) {
            printf("No\n");
            return 0;
        }
    }
    printf("Yes\n");
    return 0;
}

t4-最近的叶子(leaf)

题目描述

给你一棵带权有根树,根节点为1,且保证每个点i 的父亲pi < i,其中(pi ; i ) 的边权
为wi。
这棵树有个性质:如果我们DFS 这棵树,对于每个点都递增地枚举儿子节点,每访问
到一个节点就记录其编号,那么得到的序列刚好为1 到n。
现在有q 次询问,每次给出vi ; li ; ri,求从vi 出发到[li ; ri ] 中的其中一个叶子节点的最
短距离(保证[li ; ri ] 中至少有一个叶子节点)。(我题目的格式也不调了,比较懒)

题解

考试暴力70分,还算不错。简单讲一下:因为dfs序是连续的,那么就线段树维护答案,跟树链剖分思想差不多。

ac代码

#include <bits/stdc++.h>
#define LL long long
#define pi pair<LL,LL>
#define pii pair<int,pi>
#define fi first
#define se second
#define inf 9999999999999999
#define N 1000005
using namespace std;
vector<pi>G[N];
vector<pii>q[N];
LL a[N], ans[N], dep[N], st[N << 2], add[N << 2];
int tin[N], tout[N], tot, n, m;
void pushup(int nod) {
    st[nod] = min(st[nod << 1], st[nod << 1|1]);
}
void pushdown(int nod, int l, int r) {
    if (add[nod]) {
        add[nod<<1] += add[nod];
        add[nod<<1|1] += add[nod];
        st[nod<<1] += add[nod];
        st[nod<<1|1] += add[nod];
        add[nod] = 0;
    }
}
void build(int nod, int l, int r) {
    if (l == r) st[nod] = a[l];
    else {
        int mid = (l + r) >> 1;
        build((nod << 1), l, mid);
        build((nod << 1 | 1), mid + 1, r);
        pushup(nod);
    }
}
void update(int nod, int l, int r, int ql, int qr, LL v) {
    if (ql <= l && r <= qr) {
        add[nod] += v;
        st[nod] += v;
        return ;
    }
    pushdown(nod, l, r);
    int mid = (l + r) >> 1;
    if (ql <= mid) update(nod << 1, l, mid, ql, qr, v);
    if (qr > mid) update(nod << 1 | 1, mid + 1, r, ql, qr, v);
    pushup(nod);
}
LL query(int nod, int l, int r, int ql, int qr) {
    if (ql <= l && qr >= r) return st[nod];
    pushdown(nod, l, r);
    int mid = (l + r) >> 1;
    LL res = inf;
    if (ql <= mid) res = min(res, query(nod << 1, l, mid, ql, qr));
    if (qr > mid) res = min(res, query(nod << 1| 1, mid + 1, r, ql, qr));
    return res;
}
void dfs_init(int u) {
    ++tot; assert(tot == u);
    tin[u] = tot;
    if (G[u].size() == 0) a[u] = dep[u];
    else a[u] = inf;
    for (int i = 0; i < (int)G[u].size(); i ++) {
        int v =G[u][i].fi;
        dep[v] = dep[u] + G[u][i].se;
        dfs_init(v);
    }
    tout[u] = tot;
}
void dfs(int u) {
    for (int i = 0; i < (int)q[u].size(); i ++)
        ans[q[u][i].fi] = query(1, 1, n, q[u][i].se.fi, q[u][i].se.se) + dep[u];
    for (int i = 0; i < (int)G[u].size(); i ++) {
        int v = G[u][i].fi;
        update(1, 1, n, tin[v], tout[v], -G[u][i].se * 2);
        dfs(v);
        update(1, 1, n, tin[v], tout[v], G[u][i].se * 2);
    }
}
int main() {
    cin>> n>> m;
    for (int i = 2; i <= n; i ++) {
        int u; LL w;
        scanf("%d%lld", &u, &w);
        G[u].push_back((pi){i, w});
    }
    for (int i = 1; i <= m; i ++) {
        int u, v, w;
        scanf("%d%d%d", &w, &u, &v);
        q[w].push_back((pii){i,(pi){u, v}});
    }
    dfs_init(1);
    build(1, 1, n);
    dfs(1);
    for (int i = 1; i <= m;i ++) {
        printf("%lld\n",ans[i]);
    }
    return 0;
}