AtCoder Grand Contest 029

链接

C. Lexicographic constraints

首先二分答案,这样只需要想出一种最优的构造方法即可。

如果 ai+1>ai,那么只要 si+1si 不断向后补齐 a 即可。否则先从后往前弹出字符直到长度小于等于 ai+1,然后模拟 k 进制 +1 操作即可。可以证明这样构造一定最优。

当然直接这么做显然会超时。考虑如何更快的处理。可以发现尽管字符串长度会很长,但是其中不为 a 的个数其实是 O(n) 的,因为加入一个字符串至多会增加一个不是 a 的字符。这样我们考虑用一个单调栈维护所有非 a 的位置和字符。而模拟加法的过程可以用加法器分析,均摊是 O(n) 的。

总复杂度 O(nlogn)

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200010
using namespace std;
int ton[N],c[N],tp;
int a[N],n;
bool add(int x,int k)
{
    while(tp && ton[tp]>x) tp--;
    while(tp && ton[tp]==x && c[tp]==k) tp--,--x;
    if(!x) return false;
    if(!tp || ton[tp]!=x) ton[++tp]=x,c[tp]=2;
    else c[tp]++;
    return true;
}
bool check(int k)
{
    tp=0;bool fi=true;
    for(int i=1;i<=n;i++)
    if(i==1 || a[i]<=a[i-1])
    {
        // for(int j=1;j<=tp;j++) printf("%d,%d ",ton[j],c[j]);puts("");
        if(fi){fi=false;continue;}
        if(!add(a[i],k)) return false;
    }
    return true;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    bool inc=true;
    for(int i=2;i<=n;i++) if(a[i]<=a[i-1]) inc=false;
    if(inc){puts("1");return 0;}
    int l=2,r=n,res=1;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid-1,res=mid;
        else l=mid+1;
    }
    printf("%d",res);
    return 0;
}

E. Wandering TKHS

考虑换过来,对每一个 x 求出有多少个 y 对其有贡献。

如果一个 yx 有贡献,那么必然对 x 的子树中所有点都有贡献。所以考虑对于一个 y,其能贡献的点深度最浅的是哪个。可以证明这个点一定在 y 到根路径上。

可以发现对于一个点 x,令其到根路径上最大的点为 y,那么 y 以下一定合法,y 以上一定不合法。所以唯一问题变成 y 是否合法。

这个很好处理,记录一下次大值,看一看次大值是否在 y 上方即可。复杂度 O(n)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 200010
using namespace std;
vector<int>g[N];
int dep[N];
int mx[N],mx2[N],top[N],val[N];
void dfs(int u,int p)
{
    dep[u]=dep[p]+1;
    mx[u]=mx[p];mx2[u]=mx2[p];
    if(u>mx[u]) mx2[u]=mx[u],mx[u]=u,top[u]=u;
    else if(u>mx2[u]) mx2[u]=u;
    if(dep[mx2[u]]>dep[mx[u]]) ++val[top[u]];
    else ++val[mx[u]];
    for(int v:g[u]) if(v!=p)
    {
        top[v]=(mx[u]==u?v:top[u]);
        dfs(v,u);
    }
}
void push(int u,int p)
{
    val[u]+=val[p];
    for(int v:g[u]) if(v!=p) push(v,u);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
    dfs(1,0);push(1,0);
    for(int i=2;i<=n;i++) printf("%d ",val[i]-1);
    return 0;
}

F. Construction of a tree

题解

posted @   Flying2018  阅读(4)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示