Codeforces Round #722 (Div. 2) A~D题解
文章目录
A. Eshag Loves Big Arrays
-
题目大意
给你一串序列,你可以去掉任何大于子序列平均数的数。问你能去除最多的数量是多少?
-
解题思路
易知,子序列我们可以只选择一个,其平均数就是最小值本身,那么最后去除的即是所有大于最小值的数,所以我们易知答案即为 n − n u m m i n n- num_{min} n−nummin。
-
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 1≤i<j,∣ai−aj∣≥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, 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| ∣au−av∣。我们的目的就是要让所有的边权值之和最大。求出最大权值之和。
-
解题思路
我们知道当只有两个点 ( 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(∣rj−li∣,∣ri−lj∣)。
而有三个点 ( i , j , k ) (i,j,k) (i,j,k)的时候,这里连接顺序为 i — j — k i — j — k i—j—k,设边为 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+ri≤lj≤rj≤2lk+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} lj≤2li+ri≤rj≤2lk+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+ri≤lj≤2lk+rk≤rj,则此时的最优选择就是 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]+∣li−lj∣,dp[j][1]+∣li−rj∣)
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]+∣ri−lj∣,dp[j][1]+∣ri−rj∣)
存储树结构我们使用链式前向星,注意,这是一颗无向树,所以我们选取任意一点为根节点都可以,这里选取 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[i−1],同理, d p [ i − 2 ] , . . . d p [ 1 ] dp[i-2],...dp[1] dp[i−2],...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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具