【解题总结】2020 CCPC 绵阳站

C Code a Trie

题意:有一个 Trie,其可能被插入了一些串,每个节点(含根节点)上都有一个值,这些值互不相同。在 Trie 上查询一个串时如果找到了就返回这个串结束节点的值,否则返回最后到达的节点的值。给定若干个查询串及查询结果,问是否存在这样的 Trie,有解时这个 Trie 最少有几个节点。

对于一些串,如果它们的查询结果相同,那么对它们的查询都应在它们的 LCA 处结束。于是先对每个这样的 LCA 打标记,如果一个 LCA 下有多个值不同的标记显然无解。

做完后相当于建了一个原始的符合条件的 Trie,于是再 DFS 一遍以回收尽可能多的节点即可。


D Defuse the Bombs

最简单的方法是二分最多能活多久,稍微好一点的方法在二分的基础上发现规律,从而仅用一次排序就求出结果。

E Escape from the Island

题意:给定一个有向图,经过每条边的时间花费是 1 1 1。一个人可以重复下面的行动序列,直到到达 n n n 号点:将所有有向边视作无向边,从当前点开始移动经过至多 k k k 条边(可以不移动),到达某点后随机选择该点的某条出边(此时不可将有向边视作无向边)并沿该边移动,如果该点无出边则留在原地不动,但仍耗费 1 1 1 单位时间。问对于 ∈ [ 1 , n ] \in [1, n] [1,n] 的每一个点 i i i,这个人以 i i i 作为最初起点时,在最坏情况下,他至少要花多少时间才能到达 n n n 号点。注意只要到达了 n n n 号点就算结束。 n ≤ 1 0 5 , k ≤ 50 n \le 10^5, k \le 50 n105,k50

f ( i , j ) f(i, j) f(i,j) 表示以 i i i 为起点,此前已经移动过 j j j 条边时,到达 n n n 的最短时间。那么显然有
f ( i , j ) = { min ⁡ ( i , t ) ∈ E ∨ ( t , i ) ∈ E { f ( t , j − 1 ) + 1 } max ⁡ ( i , t ) ∈ E { f ( t , 0 ) + 1 } f(i, j) = \begin{cases} \min_{(i, t) \in E \vee (t, i) \in E}\left\lbrace f(t, j - 1) + 1\right\rbrace \\ \max_{(i, t)\in E} \left\lbrace f(t, 0) + 1\right\rbrace \end{cases} f(i,j)={ min(i,t)E(t,i)E{ f(t,j1)+1}max(i,t)E{ f(t,0)+1}
在这两种情况中取更小的那个。

于是可以从 n n n 点开始倒着做 BFS 以更新每一个状态。每次先做第一种更新,如果对于一个 f ( i , j ) f(i, j) f(i,j) 其所有后继 f ( t , 0 ) f(t, 0) f(t,0) 都计算完了但其本身还未被更新做第二种更新。

注意无出边的情况。

#include <bits/stdc++.h>
#define MAXN 100005
#define REP(temp, init_val, end_val) for (int temp = init_val; temp <= end_val; ++temp)
#define REPR(temp, init_val, end_val) for (int temp = init_val; temp >= end_val; --temp)
using namespace std;
int read(){
    
    
    int f = 1, x = 0;
    char c = getchar();
    while (c < '0' || c > '9'){
    
    if(c == '-') f = -f; c = getchar();}
    while (c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar();
    return f * x; 
}
int n, m, k, f[MAXN][55];
int at[MAXN], nxt[MAXN << 1], to[MAXN << 1], cnt;
int du[MAXN];
bool nodu[MAXN];
void init(){
    
    
    n = read(), m = read(), k = read();
    REP(i, 0, n)
        REP(j, 0, k)
            f[i][j] = 0x3f3f3f3f;
    cnt = 0;
    memset(at, 0, sizeof(int) * (n + 1));
    memset(du, 0, sizeof(int) * (n + 1));
    REP(i, 1, m){
    
    
        int u = read(), v = read();
        to[++cnt] = v, nxt[cnt] = at[u], at[u] = cnt;
        to[++cnt] = u, nxt[cnt] = at[v], at[v] = cnt;
        ++du[u];
    }
    memset(nodu, 0, sizeof(bool) * (n + 1));
    REP(i, 1, n) if (du[i] == 0) nodu[i] = true;
}
queue<pair<int, int> > q;
inline void local_update(int src, int t1, int dest, int t2){
    
    
    if (f[dest][t2] == 0x3f3f3f3f)
        f[dest][t2] = f[src][t1] + 1, q.emplace(dest, t2);
}
void solve(){
    
    
    REP(i, 0, k) f[n][i] = 0, q.emplace(n, i);
    while (!q.empty()){
    
    
        int u = q.front().first, t = q.front().second;
        q.pop();
        if (t > 0){
    
    
            for (int i = at[u]; i > 0; i = nxt[i])
                local_update(u, t, to[i], t - 1);
        } else {
    
    
            for (int i = at[u]; i > 0; i = nxt[i]){
    
    
                if (i & 1) continue ;
                --du[to[i]];
                if (!du[to[i]]){
    
    
                    REP(j, 0, k)
                        local_update(u, 0, to[i], j);
                }
            }
            if (nodu[u]){
    
    
                REP(j, 0, k)
                    local_update(u, 0, u, j);
            }
        }
    }
    REP(i, 1, n){
    
    
        printf("%d\n", (f[i][0] == 0x3f3f3f3f ? -1: f[i][0]));
    }
}
int main(){
    
    
    int T = read();
    REP(i, 1, T){
    
    
        printf("Case #%d:\n", i);
        init();
        solve();
    }
    return 0;
}

G Game of Cards

题意:有四种牌,点数各为 0、1、2、3,初始局面每种牌各有 c 0 , c 1 , c 2 , c 3 c_0, c_1, c_2, c_3 c0,c1,c2,c3 张。A 和 B 玩游戏,A 先手。当前玩家可以选择现有的牌中点数加起来不超过 3 的两张牌,将其替换成点数为它们点数之和的一张牌。不能操作者输。问谁赢。

打表再一次战胜了人类智慧。通过打表可以发现答案关于 c 0 c_0 c0 2 2 2 c 1 c_1 c1 3 3 3 成循环节。于是对于这六种情况直接输出规律即可。

要特判一些边界情况。

扫描二维码关注公众号,回复: 12091047 查看本文章

H Hide and Seek

题意:二维格点平面上有两个点,给定这两个点各自离原点的曼哈顿距离(记为 d 01 , d 02 d_{01}, d_{02} d01,d02)以及两点之间的曼哈顿距离(记为 d 12 d_{12} d12),问这两个点有多少种可能的位置 pair。

本题的推导非常依赖画图,并且情况非常多。大致可以画这样一个图。

在这里插入图片描述

方便起见,我们先假设 d 01 ≤ d 02 d_{01} \le d_{02} d01d02

  1. d 01 = 0 d_{01} = 0 d01=0。若 d 02 = d 12 > 0 d_{02} = d_{12} > 0 d02=d12>0 则答案为 4 d 02 4d_{02} 4d02,若 d 02 = d 12 = 0 d_{02} = d_{12} = 0 d02=d12=0 则答案为 1 1 1,否则答案为 0 0 0
  2. d 01 > 0 d_{01} > 0 d01>0
    1. d 12 = 0 d_{12} = 0 d12=0。若 d 02 = d 01 d_{02} = d_{01} d02=d01 则答案为 4 d 01 4d_{01} 4d01,否则答案为 0 0 0
    2. d 12 < d 02 − d 01 d_{12} < d_{02} - d_{01} d12<d02d01,则答案为 0 0 0
    3. d 12 = d 02 − d 01 d_{12} = d_{02} - d_{01} d12=d02d01,则答案为 4 ( d 12 + d 01 ( d 12 + 1 ) ) 4(d_{12} + d_{01}(d_{12} + 1)) 4(d12+d01(d12+1))
    4. d 02 − d 01 < d 12 < d 02 + d 01 d_{02} - d_{01}<d_{12}<d_{02} + d_{01} d02d01<d12<d02+d01,则考虑 d 12 − d 02 + d 01 d_{12} - d_{02} + d_{01} d12d02+d01 是否是偶数。如果不是就意味着以 d 12 d_{12} d12 为半径的“圆”不会和以 d 02 d_{02} d02 为半径的“圆”于任何一个格点相交,答案为 0 0 0。否则答案为 4 ( 2 ⋅ d 12 − d 02 + d 01 + 2 2 + 2 d 01 − 2 ) = 4 ( d 01 + d 02 + d 12 ) 4\left(2\cdot \frac{d_{12} - d_{02} + d_{01} + 2}{2} + 2d_{01} - 2\right) = 4(d_{01} + d_{02} + d_{12}) 4(22d12d02+d01+2+2d012)=4(d01+d02+d12)
    5. d 12 = d 02 + d 01 d_{12}=d_{02} + d_{01} d12=d02+d01,则答案为 4 ( d 02 + d 01 ( d 02 + 1 ) ) 4(d_{02} + d_{01}(d_{02} + 1)) 4(d02+d01(d02+1))
    6. 对于更大的 d 12 d_{12} d12,答案为 0 0 0

J Joy of Handcraft

题意:给定 n n n 个灯,第 i i i 个灯亮度为 x i x_i xi,且在 [ 2 k t i , 2 k t i + t i ] [2kt_i , 2kt_i + t_i] [2kti,2kti+ti] 时间亮,在 [ 2 k t i + t i + 1 , 2 k t i + 2 t i ] [2kt_i + t_i + 1 , 2kt_i + 2t_i] [2kti+ti+1,2kti+2ti] 时间灭,其中 k ≥ 0 , k ∈ Z k \ge 0, k \in \mathbb{Z} k0,kZ。问对于 ∈ [ 1 , m ] \in [1, m] [1,m] 的每一个时刻 j j j,没灭的灯泡中最亮的有多亮。

比较简单的做法是考虑每一个时刻会被哪些灯泡更新,这是一个经典的数论分块问题,可以结合 ST 表在 O ( m log ⁡ m + m m ) O(m \log m + m\sqrt{m}) O(mlogm+mm ) 的时间内做出。常数小的话并不难卡过去。

出题人给的另一个更好的方法是将每个周期按亮度排序,然后从大到小更新每一个时刻的答案,被更新完的时刻直接删掉。结合并查集就可以做到 O ( m log ⁡ m + m α ( m ) ) O(m \log m + m\alpha(m)) O(mlogm+mα(m))

K Knowledge is Power

题意:多次询问,每次给定一个 n n n,问所有将 n n n 拆分成若干个(至少两个)两两互质的数的拆分方案中,拆出的最大数和最小数的差,最小是多少。 5 ≤ n ≤ 1 0 9 5\le n \le 10^9 5n109

分类讨论,对于 6 6 6 无这样的拆分方案。对于奇数可以直接拆成两个相邻的自然数,对于形如 4 k ( k ∈ Z + ) 4k(k \in \mathbb{Z}^+) 4k(kZ+) 的偶数拆成 2 k − 1 , 2 k + 1 2k-1, 2k+1 2k1,2k+1 是最优的。

对于其他偶数,暴力打表发现答案 ≤ 4 \le 4 4。于是枚举情况解方程判断即可。

L Lottery

题意:给定一个可重集 S S S,为 2 a i 2^{a_i} 2ai 的数有 x i x_i xi 个。问 ∣ { ∑ t ∈ T t : T ⊂ S } ∣ |\left\lbrace \sum_{t \in T} t : T \subset S\right\rbrace| { tTt:TS},即用 S S S 中的数可以拼出多少个不同的数。

学过多重背包就知道 x i x_i xi 2 a i 2^{a_i} 2ai 可以等价地用一些 2 a i , 2 a i + 1 , ⋯ 2^{a_i}, 2^{a_i+1}, \cdots 2ai,2ai+1, 表示,其中每个数的出现次数均不超过 2。

这么转换后会发现所有出现过的 2 的指数形成了若干个连续段(如 2 a i , 2 a i + 1 , 2 a i + 2 , ⋯ 2^{a_i}, 2^{a_i+1}, 2^{a_i+2}, \cdots 2ai,2ai+1,2ai+2,),每一个段的答案乘起来就是答案,并且段内可以用 DP 计算贡献。于是就做完了。

A A Colorful Grid

有点妙,找机会再补。

F Fracture Ray

有点难写,找机会再补。

I Invaluable Assets

有点没看懂题,找机会再补。

小结

比赛前一天晚上还在赶作业,本来以为要完蛋了,实际上确实完蛋了(还好队友捞了我一把)。

H 这个题本来应该在最后一小时顺利推出的,结果脑子乱了,推了半天推不出来…

E 最后一小时看了下题目,但没有很快想到拆点的做法…

希望 ICPC 的时候不要这么捞吧。

猜你喜欢

转载自blog.csdn.net/zqy1018/article/details/109435078