「解题报告」CSP - S 2022

[CSP-S 2022] 假期计划#

题目描述#

小熊的地图上有 n 个点,其中编号为 1 的是它的家、编号为 2,3,,n 的都是景点。部分点对之间有双向直达的公交线路。如果点 xz1z1z2、……、zk1zkzky 之间均有直达的线路,那么我们称 xy 之间的行程可转车 k 次通达;特别地,如果点 xy 之间有直达的线路,则称可转车 0 次通达。

很快就要放假了,小熊计划从家出发去 4不同的景点游玩,完成 5 段行程后回家:家 景点 A 景点 B 景点 C 景点 D 家且每段行程最多转车 k 次。转车时经过的点没有任何限制,既可以是家、也可以是景点,还可以重复经过相同的点。例如,在景点 A 景点 B 的这段行程中,转车时经过的点可以是家、也可以是景点 C,还可以是景点 D 家这段行程转车时经过的点。

假设每个景点都有一个分数,请帮小熊规划一个行程,使得小熊访问的四个不同景点的分数之和最大。

输入格式#

第一行包含三个正整数 n,m,k,分别表示地图上点的个数、双向直达的点对数量、每段行程最多的转车次数。

第二行包含 n1 个正整数,分别表示编号为 2,3,,n 的景点的分数。

接下来 m 行,每行包含两个正整数 x,y,表示点 xy 之间有道路直接相连,保证 1x,yn,且没有重边,自环。

输出格式#

输出一个正整数,表示小熊经过的 4 个不同景点的分数之和的最大值。

样例 #1#

样例输入 #1#

8 8 1
9 7 1 8 2 3 6
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 1

样例输出 #1#

27

样例 #2#

样例输入 #2#

7 9 0
1 1 1 2 3 4
1 2
2 3
3 4
1 5
1 6
1 7
5 4
6 4
7 4

样例输出 #2#

7

提示#

【样例解释 #1】

当计划的行程为 123571 时,4 个景点的分数之和为 9+7+8+3=27,可以证明其为最大值。

行程 135781 的景点分数之和为 24、行程 132871 的景点分数之和为 25。它们都符合要求,但分数之和不是最大的。

行程 123581 的景点分数之和为 30,但其中 58 至少需要转车 2 次,因此不符合最多转车 k=1 次的要求。

行程 123231 的景点分数之和为 32,但游玩的并非 4 个不同的景点,因此也不符合要求。

【样例 #3】

见附件中的 holiday/holiday3.inholiday/holiday3.ans

【数据范围】

对于所有数据,保证 5n25001m100000k100,所有景点的分数 1si1018。保证至少存在一组符合要求的行程。

测试点编号 n m k
13 10 20 0
45 10 20 5
68 20 50 100
911 300 1000 0
1214 300 1000 100
1517 2500 10000 0
1820 2500 10000 100

预处理出每个点附近离家近(可直接从家到到达的地方)且点的权值最大的点,点的个数控制在 3 即可,将这些点放入集合 s 中。

先枚举 b,c,在确保 b,c 可以互相到达的情况下,再通过 sb 来枚举 a,通过 sc 来枚举 d,最后再判断 a,b,c,d 是否有重复,没有重复则进行答案的比较和更新即可。

//The code was written by yifan, and yifan is neutral!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

template<typename T>
inline T read() {
    T x = 0;
    bool fg = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        fg |= (ch == '-');
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return fg ? ~x + 1 : x;
}

const int N = 2510;

int n, m, k;
ll s[N], dis[N];
vector<int> e[N];
bool ok[N][N];
vector<int> _s[N];

void bfs(int x) {
    rep (i, 1, n, 1) {
        dis[i] = -1;
    }
    queue<int> q;
    q.emplace(x);
    dis[x] = 0;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        if (u != x) {
            ok[x][u] = 1;
            if (x != 1 && ok[1][u]) {
                _s[x].emplace_back(u);
                sort(_s[x].begin(), _s[x].end(), [](int x, int y) {
                    return s[x] > s[y];
                });
            }
            if (_s[x].size() > 3) {
                _s[x].pop_back();
            }
        }
        if (dis[u] == k + 1)    continue ;
        for (int v : e[u]) {
            if (dis[v] == -1) {
                dis[v] = dis[u] + 1;
                q.emplace(v);
            }
        }
    }
}

int main() {
    n = read<int>(), m = read<int>(), k = read<int>();
    rep (i, 2, n, 1) {
        s[i] = read<ll>();
    }
    int x, y;
    rep (i, 1, m, 1) {
        x = read<int>(), y = read<int>();
        e[x].emplace_back(y);
        e[y].emplace_back(x);
    }
    rep (i, 1, n, 1) {
        bfs(i);
    }
    ll ans = 0;
    rep (b, 2, n, 1) {
        rep (c, 2, n, 1) {
            if (!ok[b][c])  continue ;
            for (int a : _s[b]) {
                for (int d : _s[c]) {
                    if (a != d && a != c && d != b) {
                        ans = max(ans, s[a] + s[b] + s[c] + s[d]);
                    }
                }
            }
        }
    }
    cout << ans << '\n';
    return 0;
}

[CSP-S 2022] 策略游戏#

题目描述#

小 L 和小 Q 在玩一个策略游戏。

有一个长度为 n 的数组 A 和一个长度为 m 的数组 B,在此基础上定义一个大小为 n×m 的矩阵 C,满足 Cij=Ai×Bj。所有下标均从 1 开始。

游戏一共会进行 q 轮,在每一轮游戏中,会事先给出 4 个参数 l1,r1,l2,r2,满足 1l1r1n1l2r2m

游戏中,小 L 先选择一个 l1r1 之间的下标 x,然后小 Q 选择一个 l2r2 之间的下标 y。定义这一轮游戏中二人的得分是 Cxy

小 L 的目标是使得这个得分尽可能大,小 Q 的目标是使得这个得分尽可能小。同时两人都是足够聪明的玩家,每次都会采用最优的策略。

请问:按照二人的最优策略,每轮游戏的得分分别是多少?

输入格式#

第一行输入三个正整数 n,m,q,分别表示数组 A,数组 B 的长度和游戏轮数。

第二行:n 个整数,表示 Ai,分别表示数组 A 的元素。

第三行:m 个整数,表示 Bi,分别表示数组 B 的元素。

接下来 q 行,每行四个正整数,表示这一次游戏的 l1,r1,l2,r2

输出格式#

输出共 q 行,每行一个整数,分别表示每一轮游戏中,小 L 和小 Q 在最优策略下的得分。

样例 #1#

样例输入 #1#

3 2 2
0 1 -2
-3 4
1 3 1 2
2 3 2 2

样例输出 #1#

0
4

样例 #2#

样例输入 #2#

6 4 5
3 -1 -2 1 2 0
1 2 -1 -3
1 6 1 4
1 5 1 4
1 4 1 2
2 6 3 4
2 5 2 3

样例输出 #2#

0
-2
3
2
-1

提示#

【样例解释 #1】

这组数据中,矩阵 C 如下:

[003468]

在第一轮游戏中,无论小 L 选取的是 x=2 还是 x=3,小 Q 都有办法选择某个 y 使得最终的得分为负数。因此小 L 选择 x=1 是最优的,因为这样得分一定为 0

而在第二轮游戏中,由于小 L 可以选 x=2,小 Q 只能选 y=2,如此得分为 4

【样例 #3】

见附件中的 game/game3.ingame/game3.ans

【样例 #4】

见附件中的 game/game4.ingame/game4.ans

【数据范围】

对于所有数据,1n,m,q105109Ai,Bi109。对于每轮游戏而言,1l1r1n1l2r2m

测试点编号 n,m,q 特殊条件
1 200 1, 2
2 200 1
3 200 2
45 200
6 1000 1, 2
78 1000 1
910 1000 2
1112 1000
13 105 1, 2
1415 105 1
1617 105 2
1820 105

其中,特殊性质 1 为:保证 Ai,Bi>0
特殊性质 2 为:保证对于每轮游戏而言,要么 l1=r1,要么 l2=r2


我的做法是分类讨论了 9 种情况。

  1. A,B 全为正数,答案是 Amax×Bmax

  2. A 全是负数,B 全是正数,答案是 Amax×Bmax

  3. A 全是正数,B 全是负数,答案是 Amin×Bmin

  4. A,B 全是负数,答案是 Amin×Bmax

  5. A 有正有负,B 全是正数,答案是 Amax×Bmin

  6. A 有正有负,B 全是负数,答案是 max{(+)Amin×Bmin,()Amin×Bmax}

  7. A 全是正数,B 有正有负,答案是 Amin×Bmin

  8. A 全是负数,B 有正有负,答案是 Amax×Bmax

  9. A,B 均有正有负,答案为 max{()Amax×Bmax,(+)Amin×Bmin}

然后用线段树进行维护即可。

//The code was written by yifan, and yifan is neutral!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

template<typename T>
inline T read() {
    T x = 0;
    bool fg = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        fg |= (ch == '-');
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return fg ? ~x + 1 : x;
}

const int N = 1e5 + 5;

using ask = tuple<int, int, int, int>;

int n, m, q;
bool xz1 = 1, xz2 = 1;
int lg[N];
ll a[N], b[N];
ll sta_max[N][20], sta_min[N][20], stb_max[N][20], stb_min[N][20];
vector<ask> Q;

#define ls (u << 1)
#define rs (u << 1 | 1)

struct seg {
    ll val1, val0;
} t[N << 2];

void pushup(int u) {
    t[u].val1 = min(t[ls].val1, t[rs].val1);
    t[u].val0 = max(t[ls].val0, t[rs].val0);
}

void build(int u, int l, int r) {
    if (l == r) {
        if (a[l] > 0) {
            t[u].val1 = a[l];
            t[u].val0 = -1e9;
        } else if (a[l] == 0) {
            t[u].val1 = t[u].val0 = 0;
        } else {
            t[u].val0 = a[l];
            t[u].val1 = 1e9;
        }
        return ;
    }
    int mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    pushup(u);
}

int query_fu(int u, int l, int r, int lr, int rr) {
    if (lr <= l && r <= rr) {
        return t[u].val0;
    }
    int mid = (l + r) >> 1;
    ll val1 = -1e9, val2 = -1e9;
    if (lr <= mid) {
        val1 = query_fu(ls, l, mid, lr, rr);
    }
    if (rr > mid) {
        val2 = query_fu(rs, mid + 1, r, lr, rr);
    }
    return max(val1, val2);
}

int query_zh(int u, int l, int r, int lr, int rr) {
    if (lr <= l && r <= rr) {
        return t[u].val1;
    }
    int mid = (l + r) >> 1;
    ll val1 = 1e9, val2 = 1e9;
    if (lr <= mid) {
        val1 = query_zh(ls, l, mid, lr, rr);
    }
    if (rr > mid) {
        val2 = query_zh(rs, mid + 1, r, lr, rr);
    }
    return min(val1, val2);
}

int st_ask(int l, int r, int** st, bool mxn) {
    int g = lg[r - l + 1];
    if (mxn) {
        return max(st[l][g], st[r - (1 << g) + 1][g]);
    } else {
        return min(st[l][g], st[r - (1 << g) + 1][g]);
    }
    return 0;
}

void solve1() {
    int l_1, r_1, l_2, r_2;
    ll maxx, minn, da, db;
    for (ask it : Q) {
        tie(l_1, r_1, l_2, r_2) = it;
        da = lg[r_1 - l_1 + 1];
        db = lg[r_2 - l_2 + 1];
        maxx = max(sta_max[l_1][da], sta_max[r_1 - (1 << da) + 1][da]);
        minn = min(stb_min[l_2][db], stb_min[r_2 - (1 << db) + 1][db]);
        cout << maxx * minn << '\n';
    }
}

void solve2() {
    int l_1, r_1, l_2, r_2;
    ll val, g;
    for (ask it : Q) {
        tie(l_1, r_1, l_2, r_2) = it;
        if (l_1 == r_1) {
            g = lg[r_2 - l_2 + 1];
            if (a[l_1] > 0) {
                val = min(stb_min[l_2][g], stb_min[r_2 - (1 << g) + 1][g]);
            } else {
                val = max(stb_max[l_2][g], stb_max[r_2 - (1 << g) + 1][g]);
            }
            cout << a[l_1] * val << '\n';
        } else {
            g = lg[r_1 - l_1 + 1];
            if (b[l_2] > 0) {
                val = max(sta_max[l_1][g], sta_max[r_1 - (1 << g) + 1][g]);
            } else {
                val = min(sta_min[l_1][g], sta_min[r_1 - (1 << g) + 1][g]);
            }
            cout << b[l_2] * val << '\n';
        }
    }
}

int main() {
    n = read<int>(), m = read<int>(), q = read<int>();
    lg[0] = -1;
    rep (i, 1, n, 1) {
        a[i] = read<int>();
        xz1 &= (a[i] > 0);
    }
    rep (i, 1, m, 1) {
        b[i] = read<int>();
        xz1 &= (b[i] > 0);
    }
    rep (i, 1, n, 1) {
        sta_max[i][0] = a[i];
        sta_min[i][0] = a[i];
    }
    rep (i, 1, max(n, m), 1) {
        lg[i] = lg[i >> 1] + 1;
    }
    rep (j, 1, 19, 1) {
        int k;
        rep (i, 1, n, 1) {
            k = i + (1 << j) - 1;
            if (k > n)  break ;
            sta_max[i][j] = max(sta_max[i][j - 1], sta_max[i + (1 << (j - 1))][j - 1]);
            sta_min[i][j] = min(sta_min[i][j - 1], sta_min[i + (1 << (j - 1))][j - 1]);
        }
    }
    rep (i, 1, m, 1) {
        stb_min[i][0] = b[i];
        stb_max[i][0] = b[i];
    }
    rep (j, 1, 19, 1) {
        int k;
        rep (i, 1, m, 1) {
            k = i + (1 << j) - 1;
            if (k > m)  break ;
            stb_max[i][j] = max(stb_max[i][j - 1], stb_max[i + (1 << (j - 1))][j - 1]);
            stb_min[i][j] = min(stb_min[i][j - 1], stb_min[i + (1 << (j - 1))][j - 1]);
        }
    }
    int l_1, r_1, l_2, r_2;
    rep (i, 1, q, 1) {
        l_1 = read<int>(), r_1 = read<int>();
        l_2 = read<int>(), r_2 = read<int>();
        Q.emplace_back(l_1, r_1, l_2, r_2);
        xz2 &= ((l_1 == r_1) || (l_2 == r_2));
    }
    if (xz1) {
        solve1();
        return 0;
    }
    if (xz2) {
        solve2();
        return 0;
    }
    build(1, 1, n);
    for (ask it : Q) {
        tie(l_1, r_1, l_2, r_2) = it;
        ll mina = min(sta_min[l_1][lg[r_1 - l_1 + 1]], sta_min[r_1 - (1 << lg[r_1 - l_1 + 1]) + 1][lg[r_1 - l_1 + 1]]);
        ll maxa = max(sta_max[l_1][lg[r_1 - l_1 + 1]], sta_max[r_1 - (1 << lg[r_1 - l_1 + 1]) + 1][lg[r_1 - l_1 + 1]]);
        ll minb = min(stb_min[l_2][lg[r_2 - l_2 + 1]], stb_min[r_2 - (1 << lg[r_2 - l_2 + 1]) + 1][lg[r_2 - l_2 + 1]]);
        ll maxb = max(stb_max[l_2][lg[r_2 - l_2 + 1]], stb_max[r_2 - (1 << lg[r_2 - l_2 + 1]) + 1][lg[r_2 - l_2 + 1]]);
        if (mina >= 0) {
            if (minb >= 0) {
                cout << maxa * minb << '\n';
                continue ;
            }
            if (maxb <= 0) {
                cout << mina * minb << '\n';
                continue ;
            }
            if (minb <= 0 && maxb >= 0) {
                cout << mina * minb << '\n';
                continue ;
            }
        }
        if (maxa <= 0) {
            if (minb >= 0) {
                cout << maxa * maxb << '\n';
                continue ;
            }
            if (maxb <= 0) {
                cout << mina * maxb << '\n';
                continue ;
            }
            if (minb <= 0 && maxb >= 0) {
                cout << maxa * maxb << '\n';
                continue ;
            }
        }
        if (mina <= 0 && maxa >= 0) {
            if (minb >= 0) {
                cout << maxa * minb << '\n';
                continue ;
            }
            if (maxb <= 0) {
                if (mina <= 0) {
                    cout << mina * maxb << '\n';
                } else {
                    cout << mina * minb << '\n';
                }
            }
            if (minb <= 0 && maxb >= 0) {
                ll val0 = query_fu(1, 1, n, l_1, r_1);
                ll val1 = query_zh(1, 1, n, l_1, r_1);
                cout << max(val0 * maxb, val1 * minb) << '\n';
            }
        }
    }
    return 0;
}

[CSP-S 2022] 星战#

题目描述#

在这一轮的星际战争中,我方在宇宙中建立了 n 个据点,以 m 个单向虫洞连接。我们把终点为据点 u 的所有虫洞归为据点 u 的虫洞。

战火纷飞之中这些虫洞很难长久存在,敌人的打击随时可能到来。这些打击中的有效打击可以分为两类:

  1. 敌人会摧毁某个虫洞,这会使它连接的两个据点无法再通过这个虫洞直接到达,但这样的打击无法摧毁它连接的两个据点。
  2. 敌人会摧毁某个据点,由于虫洞的主要技术集中在出口处,这会导致该据点的所有还未被摧毁的虫洞被一同摧毁。而从这个据点出发的虫洞则不会摧毁

注意:摧毁只会导致虫洞不可用,而不会消除它的存在。

为了抗击敌人并维护各部队和各据点之间的联系,我方发展出了两种特种部队负责修复虫洞:

  • A 型特种部队则可以将某个特定的虫洞修复。
  • B 型特种部队可以将某据点的所有损坏的虫洞修复。

考虑到敌人打击的特点,我方并未在据点上储备过多的战略物资。因此只要这个据点的某一条虫洞被修复,处于可用状态,那么这个据点也是可用的。

我方掌握了一种苛刻的空间特性,利用这一特性我方战舰可以沿着虫洞瞬移到敌方阵营,实现精确打击。

为了把握发动反攻的最佳时机,指挥部必须关注战场上的所有变化,为了寻找一个能够进行反攻的时刻。总指挥认为:

  • 如果从我方的任何据点出发,在选择了合适的路线的前提下,可以进行无限次的虫洞穿梭(可以多次经过同一据点或同一虫洞),那么这个据点就可以实现反击
  • 为了使虫洞穿梭的过程连续,尽量减少战舰在据点切换虫洞时的质能损耗,当且仅当只有一个从该据点出发的虫洞可用时,这个据点可以实现连续穿梭
  • 如果我方所有据点都可以实现反击,也都可以实现连续穿梭,那么这个时刻就是一个绝佳的反攻时刻。

总司令为你下达命令,要求你根据战场上实时反馈的信息,迅速告诉他当前的时刻是否能够进行一次反攻

输入格式#

输入的第一行包含两个正整数 n,m

接下来 m 行每行两个数 u,v,表示一个从据点 u 出发到据点 v 的虫洞。保证 uv,保证不会有两条相同的虫洞。初始时所有的虫洞和据点都是完好的。

接下来一行一个正整数 q 表示询问个数。

接下来 q 行每行表示一次询问或操作。首先读入一个正整数 t 表示指令类型:

  • t=1,接下来两个整数 u,v 表示敌人摧毁了从据点 u 出发到据点 v 的虫洞。保证该虫洞存在且未被摧毁。
  • t=2,接下来一个整数 u 表示敌人摧毁了据点 u。如果该据点的虫洞已全部被摧毁,那么这次袭击不会有任何效果。
  • t=3,接下来两个整数 u,v 表示我方修复了从据点 u 出发到据点 v 的虫洞。保证该虫洞存在且被摧毁。
  • t=4,接下来一个整数 u 表示我方修复了据点 u。如果该据点没有被摧毁的虫洞,那么这次修复不会有任何效果。

在每次指令执行之后,你需要判断能否进行一次反攻。如果能则输出 YES 否则输出 NO

输出格式#

输出一共 q 行。对于每个指令,输出这个指令执行后能否进行反攻。

样例 #1#

样例输入 #1#

3 6
2 3
2 1
1 2
1 3
3 1
3 2
11
1 3 2
1 2 3
1 1 3
1 1 2
3 1 3
3 3 2
2 3
1 3 1
3 1 3
4 2
1 3 2

样例输出 #1#

NO
NO
YES
NO
YES
NO
NO
NO
YES
NO
NO

提示#

【样例解释 #1】

虫洞状态可以参考下面的图片, 图中的边表示存在且未被摧毁的虫洞:

【样例 #2】

见附件中的 galaxy/galaxy2.ingalaxy/galaxy2.ans

【样例 #3】

见附件中的 galaxy/galaxy3.ingalaxy/galaxy3.ans

【样例 #4】

见附件中的 galaxy/galaxy4.ingalaxy/galaxy4.ans

【数据范围】

对于所有数据保证:1n5×1051m5×1051q5×105

测试点 n m q 特殊限制
13 10 20 50
48 103 104 103
910 5×105 5×105 5×105 保证没有 t=2t=4 的情况
1112 5×105 5×105 5×105 保证没有 t=4 的情况
1316 105 5×105 5×105
1720 5×105 5×105 5×105

我们给每个点随机一个数值,以代替它的出度,如果最后的总出度等于所有点的数值和,那么说明每个点的出度都为 1,即有环。

//The code was written by yifan, and yifan is neutral!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
typedef pair<int, int> pii;

template<typename T>
inline T read() {
    T x = 0;
    bool fg = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        fg |= (ch == '-');
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return fg ? ~x + 1 : x;
}

const int M = 5e5 + 5;

int n, m, q, tot, ans;
int s[M], w[M], cur[M];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

int main() {
	mt19937 myrand(time(0));
	n = read();
	m = read();
	rep (i, 1, n, 1) {
		w[i] = myrand();
		tot += w[i];
	}
    int a, b;
	rep (i, 1, m, 1) {
		a = read(), b = read();
		s[b] += w[a];
		cur[b] += w[a];
		ans += w[a];
	}
	q = read();
    int op;
	rep (i, 1, q, 1) {
		op = read();
		if (op == 1) {
			int u = read(), v = read();
			cur[v] -= w[u];
			ans -= w[u];
		}
		if (op == 2) {
			int v = read();
			ans -= cur[v];
			cur[v] = 0;
		}
		if (op == 3) {
			int u = read(), v = read();
			cur[v] += w[u];
			ans += w[u];
		}
		if (op == 4) {
			int v = read();
			ans += s[v] - cur[v];
			cur[v] = s[v];
		}
		if (ans != tot) {
			puts("NO");
		} else {
			puts("YES");
		}
	}
	return 0;
}

[CSP-S 2022] 数据传输#

题目背景#

请勿滥用本题评测,违者可能处以封号处罚。

题目描述#

小 C 正在设计计算机网络中的路由系统。

测试用的网络总共有 n 台主机,依次编号为 1n。这 n 台主机之间由 n1 根网线连接,第 i 条网线连接个主机 aibi。保证任意两台主机可以通过有限根网线直接或者间接地相连。受制于信息发送的功率,主机 a 能够直接将信息传输给主机 b 当且仅当两个主机在可以通过不超过 k 根网线直接或者间接的相连。

在计算机网络中,数据的传输往往需要通过若干次转发。假定小 C 需要将数据从主机 a 传输到主机 bab),则其会选择出若干台用于传输的主机 c1=a,c2,,cm1,cm=b,并按照如下规则转发:对于所有的 1i<m,主机 ci 将信息直接发送给 ci+1

每台主机处理信息都需要一定的时间,第 i 台主机处理信息需要 vi 单位的时间。数据在网络中的传输非常迅速,因此传输的时间可以忽略不计。据此,上述传输过程花费的时间为 i=1mvci

现在总共有 q 次数据发送请求,第 i 次请求会从主机 si 发送数据到主机 ti。小 C 想要知道,对于每一次请求至少需要花费多少单位时间才能完成传输。

输入格式#

输入的第一行包含三个正整数 n,Q,k,分别表示网络主机个数,请求个数,传输参数。数据保证 1n2×1051Q2×1051k3

输入的第二行包含 n 个正整数,第 i 个正整数表示 vi,保证 1vi109

接下来 n1 行,第 i 行包含两个正整数 ai,bi,表示一条连接主机 ai,bi 的网线。保证 1ai,bin

接下来 Q 行,第 i 行包含两个正整数 si,ti,表示一次从主机 si 发送数据到主机 ti 的请求。保证 1si,tinsiti

输出格式#

Q 行,每行一个正整数,表示第 i 次请求在传输的时候至少需要花费多少单位的时间。

样例 #1#

样例输入 #1#

7 3 3
1 2 3 4 5 6 7
1 2
1 3
2 4
2 5
3 6
3 7
4 7
5 6
1 2

样例输出 #1#

12
12
3

提示#

【样例解释 #1】

对于第一组请求,由于主机 4,7 之间需要至少 4 根网线才能连接,因此数据无法在两台主机之间直接传输,其至少需要一次转发;我们让其在主机 1 进行一次转发,不难发现主机 1 和主机 4,7 之间都只需要两根网线即可连接,且主机 1 的数据处理时间仅为 1,为所有主机中最小,因此最少传输的时间为 4+1+7=12

对于第三组请求,由于主机 1,2 之间只需要 1 根网线就能连接,因此数据直接传输就是最优解,最少传输的时间为 1+2=3

【样例 #2】

见附件中的 transmit/transmit2.intransmit/transmit2.ans

该样例满足测试点 2 的限制。

【样例 #3】

见附件中的 transmit/transmit3.intransmit/transmit3.ans

该样例满足测试点 3 的限制。

【样例 #4】

见附件中的 transmit/transmit4.intransmit/transmit4.ans

该样例满足测试点 20 的限制。

【数据范围】

对于所有的测试数据,满足 1n2×1051Q2×1051k31ai,bin1si,tinsiti

测试点 n Q k= 特殊性质
1 10 10 2
2 10 10 3
3 200 200 2
45 200 200 3
67 2000 2000 1
89 2000 2000 2
1011 2000 2000 3
1213 2×105 2×105 1
14 5×104 5×104 2
1516 105 105 2
1719 2×105 2×105 2
20 5×104 5×104 3
2122 105 105 3
2325 2×105 2×105 3

特殊性质:保证 ai=i+1,而 bi 则从 1,2,,i 中等概率选取。


这个题就只有 16 分,算是最难的题了,只拿满前面的暴力分就已经够了。

// The code was written by yifan, and yifan is neutral!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

template<typename T>
inline T read() {
    T x = 0;
    bool fg = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        fg |= (ch == '-');
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return fg ? ~x + 1 : x;
}

using pil = pair<ll, int>;

const int N = 2e5 + 5;

int n, q, k, cnt;
int h[N], dep[N], son[N], siz[N];
int tp[N], fa[N];
ll w[N], sum[N];

struct edge {
	int v, nxt;
} e[N << 1];

void add(int u, int v) {
	e[++ cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u, int fat) {
	dep[u] = dep[fat] + 1;
	fa[u] = fat;
	siz[u] = 1;
	w[u] += w[fat];
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fat)	continue;
		dfs(v, u);
		siz[u] += siz[v];
		if (siz[v] > siz[son[u]])	son[u] = v;
	}
}

void getpos(int u, int t) {
	tp[u] = t;
	if (!son[u])	return ;
	getpos(son[u], t);
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fa[u] || v == son[u])	continue;
		getpos(v, v);
	}
}

int lca(int x, int y) {
	while (tp[x] != tp[y]) {
		if (dep[tp[x]] < dep[tp[y]])	swap(x, y);
		x = fa[tp[x]];
	}
	return dep[x] < dep[y] ? x : y;
}

int main() {
	n = read<int>(), q = read<int>(), k = read<int>();
	rep (i, 1, n, 1) {
		w[i] = read<int>();
	}
    int a, b;
	rep (i, 1, n - 1, 1) {
		a = read<int>(), b = read<int>();
		add(a, b);
		add(b, a);
	}
	dfs(1, 0);
	getpos(1, 1);
	if (k == 1) {
		int x, y, LCA;
        rep (i, 1, q, 1) {
			x = read<int>(), y = read<int>(), LCA = lca(x, y);
			cout << w[x] + w[y] - w[LCA] - w[fa[LCA]];
            putchar('\n');
		}
	}
	return 0;
}

作者:yifan0305

出处:https://www.cnblogs.com/yifan0305/p/17709995.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载时还请标明出处哟!

posted @   yi_fan0305  阅读(159)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示