Codeforces Round #629 (Div. 3)

本文深入解析了CodeforcesRound#629(Div.3)的C、D、F题目的解题思路与技巧,包括分类讨论、颜色分配策略及高度优化算法。详细介绍了F题中如何证明最终高度为台阶而非空隙的合理性,并分享了E题中运用思维与欧拉序解决路径查询问题的高效方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Codeforces Round #629 (Div. 3)

C
带有前置条件的简单分类讨论。

D
当时卡了有点久…
题目要求there are no figures of the different types going one right after another and colored in the same color.

仔细考虑清楚这个约束条件后,发现只需要比较当前位置和前一个位置的动物是否相同,如果相同则可以染相同颜色,如果不同就染另一个颜色。按照这个策略如果先不考虑1与n的循环关系,则整个序列就大体呈现为1…12…21…1…1…;当染到第n个后发现颜色恰为1且这两个动物不同,那么我们尝试通过一些办法尽可能用更少的颜色来维持序列的合法性:如果之前出现过一次相等的情况,则将该次染色染为另一种颜色,而之后的其余情况与之前采取相同的策略,这样操作之后该后缀的颜色就全部翻转,那么1-n之间的颜色也就不相同了,ok;如果没有出现相等的情况,那么只能引入第三个颜色来打破1212…21的僵局。

F
A掉有运气的成分…
但是做完后仔细考虑了为什么这样能过靠近100个test并不断担心fst时证明了自己的结论。

首先排序。
接下来我们考虑在这个楼梯上,最终相同的那一串数的大小是多少。这一串数的数值要么是原本楼梯的某一个台阶,要么就是台阶之间的某一个其余值。做的时候莽了,直接认为就是某一个台阶,这样子不假思索去莽很容易之后心态崩,以后对于问题的考虑一定要更全面。

下面尝试说明一下为什么最终高度为一个台阶的情况一定优于为某一个空隙的情况:假设空隙高度为h,位于台阶i和台阶i+1之间,考虑选某个高度的代价在楼梯上几何大小,容易发现,取h的情况比取台阶i的情况要多花费左下角小矩形的面积,少花费右上角小矩形的面积,而取h的情况比取台阶i + 1的情况要少花费左下角小矩形的面积,多花费右上角小矩形的面积
显然在每一组比较中两个矩形的高相同,不同的只有。那么无论这一组台阶靠左还是靠右,台阶i和台阶i + 1中一定有一个的情况比高度h更优,至此可以说明最终答案取为台阶高度的合理性!

之后的问题就比较方便了,左右dp处理出来以i台阶为最终情况,左边高度全部变为 h i − 1 h_i-1 hi1的花费,右边高度全部变为 h i + 1 h_i+1 hi+1的花费。然后优先用两个单向来更新答案,如果单向上长度不够,则使用双向来更新答案。

还需要特判一下当前长度不经过任何挖山填海就已经满足k要求了,不然会WA6…


E
思维&欧拉序
值得学习的一道题!

尽管在场上看错了题目,没有注意到是从根节点1开始的一条路径,但是哪怕注意到了我觉得我当时也写不出来。

因为题目要求的这条路径从根节点1开始,所以我们找出查询节点集合中深度最深的一个点,该节点到根节点的路径一定是最终路径(如果存在的话)。找到路径后,剩下的节点要么本身就在路径上,要么与路径上某一个点相距1。容易发现,如果某个点与该路径相距1,那么只可能是其父节点恰为该路径上一个点,接下来就是要考虑如何判断所有查询集合中的点的父节点是否都在路径上。如果暴力一个个匹配显然会T,所以需要再等价的转化判断条件:如果该父节点在这条路径上,就等价于该父节点也是最深节点的父亲,这是由树上路径本身的性质所决定的。剩下的问题就是如何判断某一对点是否是祖父子关系,用欧拉序就可以超快的完成。

代码

const int maxn = 2e5 + 10;

struct star{int to, next;}edge[maxn << 1];
int head[maxn], top = 0;
int par[maxn];

void add(int u, int v){
    edge[top].to = v;
    edge[top].next = head[u];
    head[u] = top++;
}

int L[maxn], R[maxn], cnt = 0;
int vis[maxn];
int dep[maxn];
void dfs(int x){
    L[x] = cnt++;
    vis[x] = 1;
    for(int i = head[x]; ~i; i = edge[i].next){
        if(!vis[edge[i].to]){
            dep[edge[i].to] = dep[x] + 1;
            par[edge[i].to] = x;
            dfs(edge[i].to);
        }
    }
    R[x] = cnt++;
}

int query[maxn];

int check(int u, int v){
    if(L[u] <= L[v] && R[u] >= R[v]) return 1;
    return 0;
}

int main(){
//    Fast;
    memset(head, -1, sizeof head);
    int n, m; scanf("%d %d", &n, &m);
    for(int i = 0, u, v; i < n - 1; i++){
        scanf("%d %d", &u, &v);
        add(u, v); add(v, u);
    }
    dfs(1); par[1] = 1;
    
    while(m--){
        int q; scanf("%d", &q);
        int l = -1;
        for(int i = 0; i < q; i++){
            scanf("%d", query + i);
            if(l == -1 || dep[l] < dep[query[i]]) l = query[i];
            query[i] = par[query[i]];
        }
        int flag = 1;
        for(int i = 0; i < q; i++) flag &= check(query[i], l);
        if(flag) puts("YES"); else puts("NO");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值