Codeforces Round 890 (Div. 2)

太唐了,思路出的很快,卡在实现上了。

A. Tales of a Sort

题意

给你一个长度为 \(n\) 的序列 \(a\) ,每次操作可以令 \(a_i\) 替换为 \(max(a_{i-1},0)\) 。问当 \(a\) 满足 \(a_1\leq a_2\leq ...\leq a_n\) 至少需要多少次操作。

思路

题目要求递增,如果序列中出现了递减的位置那我们需要把大的位置给变成 \(0\) 才能保证序列递增。所以只要找到递减位置的最大值就行。

代码

void solve()
{
    int n;
    cin>>n;
    vector<int> a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    int maxn=0;
    for(int i=2;i<=n;i++)
    {
        if(a[i]<a[i-1]) maxn=max(maxn,a[i-1]);
    }
    cout<<maxn<<endl;
}

B. Good Arrays

题意

给你一个长度为 \(n\) 的序列 \(a\) ,问你能不能构造一个序列 \(b\) 满足:

  • \(a_i\neq b_i\)
  • \(\sum_{i=1}^{n} a_i=\sum_{i=1}^{n} b_i\)

思路

一个简单的想法就是先把所有的位置填成 \(1\) ,然后考虑分配剩余的数。那么一定要把原来 \(a\) 序列里是 \(1\) 的位置至少加上 \(1\) 。所以这题就转化成判断总和减去 \(n\) 是不是大于等于 \(a_i\)\(1\) 的个数。注意特判一下 \(n=1\) 的情况。

代码

void solve()
{
    int n,sum=0,cnt=0;
    cin>>n;
    vector<int> a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i],cnt+=(a[i]==1);
    if(n==1) cout<<"NO\n";
    else
    {
        int num=sum-n;
        if(num<cnt) cout<<"NO\n";
        else cout<<"YES\n";
    }
}

C. To Become Max

题意

给你一个长度为 \(n\) 的序列 \(a\) ,你可以进行以下操作:

  • 选择一个 \(i\) ,满足 \(1\leq i\leq n-1\)\(a_i\leq a_{i+1}\)
  • \(a_i=a_i+1\)

问你最多操作 \(k\) 次之后序列中的最大值是多少。

思路

由于 \(n\leq 1000\) ,考虑 \(n^2\) 暴力把所有区间找出来。因为答案具有单调性,最大值越大需要的操作次数越多,所以考虑二分答案。

代码

void solve()
{
    int n,k;
    cin>>n>>k;
    vector<int> a(n+2),b(n+2);
    for(int i=1;i<=n;i++) cin>>a[i];
    auto check=[&](int x)
    {
        for(int i=1;i<=n;i++)
        {
            if(a[i]>=x) return 1;
            for(int j=1;j<=n;j++) p[j]=a[j];
            int m=k;
            int op=x;
            for(int j=i;j<=n && m>=0;j++,op--)
            {
                if(p[j]>=op) return 1;
                m-=op-p[j];
            }
        }
        return 0;
    };
    int ans=0,l=1,r=1e9;
    while(l<r){
        int mid = l + r >> 1;
        if(check(mid)) ans=mid,l=mid+1;
        else r=mid;
    }
    cout<<ans<<endl;
}

E1. PermuTree (easy version)

题意

给你一棵树,让你把 \(1...n\) 填到每个节点 \(a_i\) 上。让你找出满足 \(a_i<a_{lca(i,j)}<a_j\)\((i,j)\) 最多的对数。

思路

先考虑如果这棵树是一个二叉树的情况,那么我们规定左子树的值小于根节点的值,右子树的值大于根节点的值,那么此时答案就是每个子树左右儿子大小乘积累加起来。如果不是二叉树,那么他的贡献应该是比根节点小的点的个数乘比根节点大的点的个数。那么计算一个子树的贡献,问题就转化成了集合划分问题。给你一个序列让你把这个序列里的值划分成两个集合,使得两个集合的和乘积最大。那么这就是一个经典的背包问题。

代码

void solve()
{
    int n,ans=0;cin>>n;
    vector<int> e[n+1],siz(n+1);
    for(int i=2;i<=n;i++)
    {
        int fa;cin>>fa;
        e[fa].push_back(i);
        e[i].push_back(fa);
    }
    function<void(int,int)> dfs=[&](int u,int fa)
    {
        siz[u]=1;
        vector<int> temp;
        for(auto v:e[u])
        {
            if(v==fa) continue;
            dfs(v,u);
            siz[u]+=siz[v];
            temp.push_back(siz[v]);
        }
        if(temp.size()<=1) return;
        vector<int> dp(n+1); dp[0]=1;
        int sum=0;
        for(int i=0;i<temp.size();i++)
        {
            sum+=temp[i];
            for(int j=n;j>=0;j--) if(dp[j]) dp[j+temp[i]]=1;
        }
        int maxn=0;
        for(int i=1;i<sum;i++) if(dp[i])maxn=max(maxn,i*(sum-i));
        ans+=maxn;
    };
    dfs(1,0);
    cout<<ans<<endl;
}

感觉这几场思路都挺快的,但是写代码上就会出现问题。原因可能是思路不够清晰,没有完全想清楚就开始敲代码了,导致写的时候一直修改一直重构。虽然思路出的快,但是不够清晰,争取以后思路又快又清晰。

posted @ 2024-10-10 10:23  杨丶老爹  阅读(9)  评论(0编辑  收藏  举报