Codeforces Round #722 (Div. 2) A~D题解

A. Eshag Loves Big Arrays

  • 题目大意

    给你一串序列,你可以去掉任何大于子序列平均数的数。问你能去除最多的数量是多少?

  • 解题思路

    易知,子序列我们可以只选择一个,其平均数就是最小值本身,那么最后去除的即是所有大于最小值的数,所以我们易知答案即为 n − n u m m i n n- num_{min} nnummin

  • AC代码

/**
  *@filename:A_Eshag_Loves_Big_Arrays
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-24 22:35
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int t,n;
int a[N];
int x;
void solve(){
}
int main(){
    cin >> t;
    while(t--){
        cin >> n;
        memset(a,0,sizeof(a));
        for(int i = 1;i <= n; i++){
            cin >> x;
            a[x]++;
        }
        for(int i = 1; i <= 100 ;i++){
            if(a[i]){
                cout << n - a[i] << endl;
                break;
            }
        }
    }
    solve();
    return 0;
}

B. Sifid and Strange Subsequences

  • 题目大意

    给你一串序列,你需要选取一个长度尽可能大的子序列,其中需要满足, 1 ≤ i < j , ∣ a i − a j ∣ ≥ M A X 1≤i<j,|a_i-a_j|≥MAX 1i<j,aiajMAX M A X MAX MAX为序列中的最大值。

  • 解题思路

    我们需要知道, M A X MAX MAX即为序列中的值,也就是说,我们关心的实际上就是 M A X MAX MAX进行操作之后会不会小于 M A X MAX MAX,仅考虑这个我们发现实际上所有的非正数都满足条件,这两个正数是绝对不满足的,因为它们相减得到的值是小于 M A X MAX MAX的,那么我们可能可以选择一个正数,那么选择的依据是什么呢? 我们可以对整个序列进行排序,那么我们选取的正数肯定是越小越好,同时一定要满足相邻之差大于等于 M A X MAX MAX M A X MAX MAX即我们选取的整数,所以我们可以保存选取的非正数的最小相邻之差,以这个为标准去选取符合条件的正数即可得到答案。

  • AC代码

/**
  *@filename:B_Sifid_and_Strange_Subsequences
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-24 22:42
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int t,n;
int a[N];
//此题我们易知一定包含了所有的非正数,而若要包含正数,则必须满足之差是大于等于该正数的,
//且正数一定是只能取一个。
void solve(){
    sort(a + 1,a + 1 + n);
    int cnt = 0,minn = 0x3f3f3f3f;//minn记录选择的负数之间存在的最小值之差。
    for(int i = 1; i <= n; i++){
        if(a[i] <= 0){
            cnt ++;
        }
    }
    for(int i = 2; i <= n; i++){
        if(a[i] <= 0){
            minn = min(minn,a[i] - a[i - 1]);
        }
    }
    for(int i = 1; i <= n; i++){
        if(a[i] > 0 && minn >= a[i]){
            cnt ++;
            break;
        }
    }
    cout << cnt << endl;
}
int main(){
    cin >> t;
    while(t--){
        cin >> n;
        for(int i = 1; i <= n; i++){
            cin >> a[i];
        }
        solve();
    }
    return 0;
}

C. Parsa’s Humongous Tree

  • 题目大意

    给你一棵树,树上的每个节点 i i i都有一个值域 [ l i , r i ] [l_i,r_i] [li,ri],我们需要从值域中确定一个值 a i ∈ [ l i , r i ] a_i\in [l_i,r_i] ai[li,ri],而 ( u , v ) (u,v) (u,v)边权值则为 ∣ a u − a v ∣ |a_u-a_v| auav。我们的目的就是要让所有的边权值之和最大。求出最大权值之和。

  • 解题思路

    我们知道当只有两个点 ( i , j ) (i,j) (i,j)的时候选择边界一定是最优的,即 m a x ( ∣ r j − l i ∣ , ∣ r i − l j ∣ ) max(|r_j - l_i|,|r_i - l_j|) max(rjli,rilj)

    而有三个点 ( i , j , k ) (i,j,k) (i,j,k)的时候,这里连接顺序为 i — j — k i — j — k ijk,设边为 w i j w_{ij} wij w j k w_{jk} wjk,我们目的是使得使得 w i j + w k j w_{ij}+w_{kj} wij+wkj最大,其中我们分析一下几种情况。

    • l i + r i 2 ≤ l j ≤ r j ≤ l k + r k 2 \frac{l_i+r_i}{2}\leq l_j\leq r_j \leq \frac{l_k+r_k}{2} 2li+riljrj2lk+rk,则易知此时最优的选择是 a i = l i , a j a_i=l_i,a_j ai=li,aj [ l j , r j ] [l_j,r_j] [lj,rj]任何一个值都可以,这里同样是可以取边界, a k = r k a_k=r_k ak=rk
    • l j ≤ l i + r i 2 ≤ r j ≤ l k + r k 2 l_j \leq \frac{l_i+r_i}{2}\leq r_j \leq \frac{l_k+r_k}{2} lj2li+rirj2lk+rk,则此时的最优选择就是 a i = r i , a j = l j , a k = r k a_i = r_i,a_j= l_j,a_k=r_k ai=ri,aj=lj,ak=rk
    • l i + r i 2 ≤ l j ≤ l k + r k 2 ≤ r j \frac{l_i+r_i}{2}\leq l_j\leq \frac{l_k+r_k}{2} \leq r_j 2li+rilj2lk+rkrj,则此时的最优选择就是 a i = l i , a j = r j , a k = l k a_i=l_i,a_j=r_j,a_k=l_k ai=li,aj=rj,ak=lk
    • 当$l_j \leq \frac{l_i+r_i}{2}\leq\frac{l_k+r_k}{2}\leq r_j , 则 此 时 的 最 优 选 择 是 ,则此时的最优选择是 a_i=r_i,a_j=l_j,a_k=r_k$。

    分析可知,顶点权选择边界一定是最优的,这对更多点的情况依然适用 ,所以我们实际上就是枚举每个顶点究竟该选择哪个边界,这应该就要想到树形 D P DP DP,我们假设 d p [ i ] [ 0 ] dp[i][0] dp[i][0]为选取左边界,以它为根节点所构成的子树获取的最大边权值之和,而 d p [ i ] [ 1 ] dp[i][1] dp[i][1]则为选取有边界,以它为根节点所构成的子树获取的最大边权值之和。设当前节点为 i i i,孩子节点为 j j j,则状态转移方程易知:

    d p [ i ] [ 0 ] + = m a x ( d p [ j ] [ 0 ] + ∣ l i − l j ∣ , d p [ j ] [ 1 ] + ∣ l i − r j ∣ ) dp[i][0]+=max(dp[j][0]+|l_i-l_j|,dp[j][1]+|l_i-r_j|) dp[i][0]+=max(dp[j][0]+lilj,dp[j][1]+lirj)

    d p [ i ] [ 1 ] + = m a x ( d p [ j ] [ 0 ] + ∣ r i − l j ∣ , d p [ j ] [ 1 ] + ∣ r i − r j ∣ ) dp[i][1]+=max(dp[j][0]+|r_i-l_j|,dp[j][1]+|r_i-r_j|) dp[i][1]+=max(dp[j][0]+rilj,dp[j][1]+rirj)

    存储树结构我们使用链式前向星,注意,这是一颗无向树,所以我们选取任意一点为根节点都可以,这里选取 1 1 1作为根节点。这里会卡输入,建议使用scanf和printf输入输出,或者开启IOS

  • AC代码

/**
  *@filename:C_Parsa_s_Humongous_Tree
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-25 12:30
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int t,n,l[N],r[N];
ll dp[N][2];
struct node{
    int v,next;
}edges[N << 1];
int tot,head[N];
void add(int u,int v){
    edges[++tot].v = v;
    edges[tot].next = head[u];
    head[u] = tot;
}
void dfs(int u,int fu){
    for(int i = head[u]; i; i = edges[i].next){
        int v = edges[i].v;
        if(v == fu)continue;//避免自环。
        dfs(v,u);//先更新子树。再往上更新根节点。
        dp[u][0] += max(dp[v][0] + abs(l[u] - l[v]),dp[v][1] + abs(l[u] - r[v]));
        dp[u][1] += max(dp[v][0] + abs(r[u] - l[v]),dp[v][1] + abs(r[u] - r[v]));
    }
}
void solve(){
    dfs(1,-1);
    printf("%lld\n",max(dp[1][0],dp[1][1]));
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i = 1; i <= n; i++){
            scanf("%d %d",&l[i],&r[i]);
        }
        memset(head,0,sizeof(head));
        memset(dp,0,sizeof(dp));
        tot = 0;
        int u,v;
        for(int i = 1; i < n; i++){
            scanf("%d %d",&u,&v);
            add(u,v),add(v,u);
        }
        solve();
    }
    return 0;
}

D. Kavi on Pairing Duty

  • 题目大意

    在数轴上有 2 n 2n 2n各点,分别是 1 , . . . , 2 n 1,...,2n 1,...,2n,良好配对数为选择的点对构成的区间 A , B A,B A,B,其中 A A A的长度等于 B B B的长度,或者 A A A包含 B B B。问这最大的配对数。

  • 解题思路

    我们设 d p [ i ] dp[i] dp[i]表示这 [ 1 , 2 i ] [1,2i] [1,2i]区间最大的良好配对数目。则易知 d p [ n ] dp[n] dp[n]就是答案。题目中良好配对存在两种,一种是包含情况,一种是等长情况。

    • 对于包含情况,我们知道对于区间 [ i , 2 i ] [i,2i] [i,2i],我们将两边点按等长选择上以后,中间的点就可以随便选取了,即如果我们选取 ( i , 2 i ) (i,2i) (i,2i)那么之间的点对一定包含在这个点对里,即 d p [ i − 1 ] dp[i-1] dp[i1],同理, d p [ i − 2 ] , . . . d p [ 1 ] dp[i-2],...dp[1] dp[i2],...dp[1]都在其中。
    • 而对于等长情况,区间长度能被点对长度整除,就能选择,即 i i i的正约数的集合。

    则我们的状态转移方程易知: d p [ i ] = ∑ k = 1 i d p [ k ] + v [ k ] dp[i] = \sum_{k= 1}^idp[k]+v[k] dp[i]=k=1idp[k]+v[k],其中 v [ k ] v[k] v[k]即为 k k k的正约数集合。

  • AC代码

/**
  *@filename:D_Kavi_on_Pairing_Duty
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-25 14:54
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 1000000 + 5;
const int P = 998244353;

int dp[N],v[N];
int n;
void solve(){
}
int main(){
    cin >> n;
    for(int i = 1; i <= n; i++){
        for(int j = i; j <= n; j+=i){
            v[j] ++;
        }
    }
    int sum = 1;
    dp[1] = 1;
    for(int i = 2; i <= n; i++){
        dp[i] = (v[i] + sum) % P;
        sum = (sum + dp[i]) % P;
    }
    cout << dp[n] << endl;
    solve();
    return 0;
}
posted @   unique_pursuit  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示