A. 吔我大炮!

By Hetmes Askalana *

题意分析

给定三个正整数 ,判断是否满足

题解

直接按上文所述判断即可。

代码

void solve(){
    LL a, b, c;
    cin >> a >> b >> c;
    if(a * b <= c) yes; else no;
    return;
}

B(B1). 梦间

By Hetmes Askalana *

题意分析

原题很直白了,不分析了。

题解

首先明确中线到底是什么:三角形的一条边的中点和对点的连线。如果中线在坐标轴上,那么这个对点一定在坐标轴上,且对边的中点也在坐标轴上,那么就有: 或者 (假设对点是 号点)

其他同理。

代码

void solve(){
    int x1, x2, x3, y1, y2, y3;
    cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;
    if(
        (y1 == 0 and (y2 + y3) == 0) or
        (x1 == 0 and (x2 + x3) == 0) or
        (y2 == 0 and (y1 + y3) == 0) or
        (x2 == 0 and (x1 + x3) == 0) or
        (x3 == 0 and (x1 + x2) == 0) or
        (y3 == 0 and (y1 + y2) == 0)
    )yes; else no;
    return;
}

C(B2). 坠入

By Hetmes Askalana *

题意分析

原题很直白了,不分析了。

题解

如果一条直线平行于一个坐标轴,那么这条直线上所有与该轴垂直的轴上的的坐标值都相同,也就是: 其等价于

其他同理。

代码

void solve(){
    int x1, x2, x3, y1, y2, y3;
    cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;
    if(
        (2 * y1 == y2 + y3 or 2 * x1 == x2 + x3) or
        (2 * y2 == y1 + y3 or 2 * x2 == x1 + x3) or
        (2 * y3 == y1 + y2 or 2 * x3 == x1 + x2)
    )yes; else no;
    return;
}

D. 漫步

By Hetmes Askalana *

题意分析

给定一个 ,找到一个 使得 中为 的二进制位在 中也为 。换言之,

题解

假设 ,在 时,,我们需要保证在现有的 不变的情况下进行操作,一定会比造成进位要强。同时因为 的约束,我们只能选择比 的二进制长度短的 ,那么自然,全是 型的肯定不行。

对于其他的,我们只需要随便选择一个 中为 的二进制位,构建一个只有这个位置是 即可过关。

代码

#define neg {cout << -1 << endl; return;}
void solve(){
    LL x; cin >> x;
    if(!(x & (x + 1))) neg;
    for(int i = 1; i < x; (i <<= 1)){
        if((x & (i + x)) == x){
            cout << i << endl;
            return;
        }
    }
    neg;
    return;
}

E. 密藏

By Hetmes Askalana *

题意分析

有两个轴,从一号轴的一号点开始,每次前进一格,并在钱够的情况下可以选择消耗 点金币跃迁到另一条轴,然后拾起这个点的金币。求抵达 号点并拾起金币后持有的金币最大值。

题解

假设表世界是 ,里世界是 ,令 表示在 世界上抵达 号点时的最大金币,令 初始为 不可达,那么从 开始有转移方程

下面的代码没有极致压缩看上去比较正常

代码

void solve(){
    LL n, k;
    cin >> n >> k;
    vector<LL> a(n + 1), b(n + 1);
    for(int i = 1; i <= n; ++i) cin >> a[i];
    for(int i = 1; i <= n; ++i) cin >> b[i];

    vector<vector<LL>> f(n + 1, vector<LL>(2, LLONG_MIN));
    f[0][0] = f[0][1] = 0;
    f[1][0] = a[1];
    for(int i = 2; i <= n; ++i){
        f[i][0] = max(
            f[i - 1][0] + a[i],
            (f[i - 1][1] >= k ? f[i - 1][1] + a[i] - k : LLONG_MIN)
        );
        f[i][1] = max(
            f[i - 1][1] + b[i],
            (f[i - 1][0] >= k ? f[i - 1][0] + b[i] - k : LLONG_MIN)
        );
    }
    cout << max(f[n][0], f[n][1]) << endl;
    return;
}

F. 回响

By Hetmes Askalana *

题意分析

给定一个序列,在保持原序列中不为 的地方不变的情况下,将 的位置填上数字,使得

题解

不需要太多思考,填就完事了。

对于每个区间,我们使其趋近于区间右端点的那个数,如果小了就 ,大了就 ,一样的话就往上加一加。如果最后构建出来的不满足上面的条件,就不行,如果满足,就行。

代码

void solve(){
    int n; cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; ++i) cin >> a[i];
    int pm = 0, j = 0;
    if(a[0] == 0){
        while(pm < n and a[pm] == 0) ++pm;
        if(pm == n) for(int i = 0; i < n; ++i) a[i] = i + 1;
        else for(int i = pm - 1; i >= 0; --i) if(a[i + 1] > 1) a[i] = a[i + 1] - 1; else a[i] = a[i + 1] + 1;
    } // 对付首位为0的情况
    bool inConstruct = false, flag = true;
    for(int i = pm; i < n; ++i){
        if(a[i] == 0){
            if(!inConstruct){
                j = i;
                while(j < n and a[j] == 0) ++j;
                inConstruct = true;
            } //找到区间左端点,搜出区间右端点,开始构造模式
            if(a[i - 1] > a[j] and j != n) a[i] = a[i - 1] - 1;
            else a[i] = a[i - 1] + 1;
        }else inConstruct = false;
    }
    for(int i = 1; i < n; ++i) flag &= (abs(a[i] - a[i - 1]) == 1); //判定一下
    if(!flag) neg;
    for(int i = 0; i < n; ++i) cout << a[i] << " ";
    cout << endl;
    return;
}

G. 升~龙~~

By Hetmes Askalana *

题意分析

给定一棵根为 的树,定义其权值为所有到叶子节点的简单路径上的点权之和的最大值,每次操作可以选择 ,使 断开与其父的链接,并认 做父。求这个操作下的树的权值,每次操作独立。

题解

我们首先搜出从根到各个节点的最大权值(这样到每个叶子的权值都能定下来)( 数组中)和以 为根且不包含其父的树的子树的权值( 数组中),由于dfs的性质,我们在搜的时候顺便记录一下在dfn序下,一个节点什么时候被搜到和什么时候搜完它的全部子节点。

对于每次操作,我们首先把 和所有子节点抠掉,就是抠掉 在dfn序中进入dfs和搜完所有子节点离开dfs的点所构成的区间。之后剩下的所有可以抵达的节点的最大值就是被抠掉 剩下的树的权值。

之后输出剩下的树的权值的最大值和 即可。

代码

void solve(){
    LL n, q, x, y, cnt = 0; cin >> n >> q;
    vector<LL> a(n + 10, 0), v(n + 10, 0), pv(n + 10, LLONG_MIN), f(n + 10, 0), dp(n + 10, LLONG_MIN), ds(n + 10, LLONG_MIN), *** + 10, 0), op(n + 10, 0);
    vector<vector<int>> G(n + 10);
    for(int i = 1; i <= n; ++i) cin >> a[i];
    for(int i = 2; i <= n; ++i){
        cin >> x;
        G[x].push_back(i);
    }
    auto dfs = [&](auto &&self, int np, int p) -> void {
        ***p] = ++cnt; // x进入
        if(~p) v[np] = v[p] + a[np]; else v[np] = a[np]; // 继承父节点往下
        f[np] = a[np];
        for(auto to : G[np]){
            self(self, to, np);
            f[np] = max(f[np], f[to] + a[np]); // 类似树形dp的东西
        }
        op[np] = cnt; // x离开
    };
    dfs(dfs, 1, -1);
    for(int i = 1; i <= n; ++i) pv[vp[i]] = v[i]; // 同步dfn序
    dp[1] = pv[1], ds[n] = pv[n]; // 下面都是前后缀最大值
    for(int i = 2; i <= n; ++i) dp[i] = max(dp[i - 1], pv[i]);
    for(int i = n - 1; i >= 1; --i) ds[i] = max(ds[i + 1], pv[i]);
    while(q--){
        cin >> x >> y;
        LL left = LLONG_MIN;
        if(vp[x] > 1) left = max(left, dp[vp[x] - 1]);
        if(op[x] < n) left = max(left, ds[op[x] + 1]);
        cout << max(left, v[y] + f[x]) << endl;
    }
    return;
}