首页 > 其他分享 >【题解】Codeforces Round 890(CF1856)

【题解】Codeforces Round 890(CF1856)

时间:2023-08-06 21:22:08浏览次数:34  
标签:890 CF1856 return int 题解 最大值 mid long sum

赛时过了 A-E1,rk195
可惜是 E2 傻逼了不会背包优化了,直接连普及组水平都不到了。

A.Tales of a Sort

题目描述:

给定长度为 \(n\) 的序列 \(a\),每次操作为对于所有 \(i\) 将 \(a_i\) 变为 \(\max(a_i-1,0)\),询问最少多少次操作之后可以让序列 \(a\) 单调不降。

题目分析:

唯一能改变元素大小关系的就是将数变成 \(0\),所以可以考虑找到结尾的最长不降序列,然后将其前面的全部变成 \(0\) 就好了。
答案就是这些数里的最大值。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
int a[N];
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%lld",&T);
	while(T--){
		int n;scanf("%lld",&n);
		for(int i=1; i<=n; i++)	scanf("%lld",&a[i]);
		int pos = 0;
		for(int i=n; i>=1; i--){
			if(a[i-1] > a[i]){
				pos = i;break;
			}
		}
		int ans = 0;
		for(int i=1; i<pos; i++)	ans = max(ans,a[i]);
		printf("%lld\n",ans);
	}
	return 0;
}

B.Good Arrays

题目描述:

给定一个长度为 \(n\) 的序列 \(a\),询问是否存在一个长度为 \(n\) 的序列 \(b\) 满足:

  • \(\forall i\in [1,n]\) \(a_i \not= b_i\)
  • \(\sum_{i=1}^n a_i = \sum_{i=1}^n b_i\)

题目分析:

考虑调整,一定是一些数变大另一些数变小。
所以直接将所有 \(a_i = 1\) 的位置的 \(b_i\) 设为 \(2\) 其余部分的 \(b_i\) 设为 \(1\),显然这一定是一个和最小的方案。
如果此时 \(\sum_i^n b_i > \sum_{i}^n a_i\),那么也就无解了。
否则如果 \(\sum_i^n b_i \le \sum_i^n a_i\),就可以通过调整 \(a_i = 1\) 的位置对应的 \(b_i\) 的值使得和满足条件。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
int a[N],b[N];
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%lld",&T);
	while(T--){
		int n;scanf("%lld",&n);
		int sum = 0;
		for(int i=1; i<=n; i++)	scanf("%lld",&a[i]),sum += a[i];
		if(n == 1){
			printf("NO\n");
			continue;
		}
		sort(a+1,a+n+1);
		int tmp = 0;
		for(int i=1; i<=n; i++){
			if(a[i] == 1)	b[i] = 2;
			else	b[i] = 1;
			tmp += b[i];
		}
		if(tmp <= sum){
			printf("YES\n");
			continue;
		}
		if(tmp > sum){
			printf("NO\n");
			continue;
		}
	}
	return 0;
}

C.To Become Max

题目描述:

给定一个长度为 \(n\) 的序列 \(a\) 你可以执行至多 \(k\) 次操作,一次操作的定义如下:

  • 选择一个位置 \(i \in [1,n-1]\) 满足 \(a_i \le a_{i+1}\),将 \(a_i\) 加 \(1\)。

询问进行操作后的最大的 \(\max_{i=1}^n a_i\) 的值。
\(n \le 1000,k \le 10^8\)

题目分析:

考虑我们若最大值的位置为 \(i\) 且其变成了 \(x\),那么 \([i,i+n]\) 的序列大概会长成 \(x,x-1,x-2,...\) 的样子。
所以可以直接二分答案,然后枚举钦定哪个位置为最大值,然后计算一下需要多少次操作。
但是其实我们没有必要每一次都计算到 \(n\),假设位置 \(j\) 处应该为 \(p\) 但是 \(a_j \ge p\) 那么就后面其实就没必要算了,直接从这个位置向左扩展就好了。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5+5;
int n,a[N],b[N],k;
bool chk(int x){
	for(int i=1; i<=n; i++){  //将这个位置改为 x
//		if(a[i] >= x)	return true; 
		int tmp = 0;
		bool flag = false;
		for(int j=i; j<=n; j++){
			//j : x - (j - i)
			tmp += max(0ll,(x - (j - i)) - a[j]);
			if(a[j] >= (x - (j - i))){
				flag = true;break;
			}
		}
		if(tmp <= k && flag)	return true;
	}
	return false;
}
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%lld",&T);
	while(T--){
		scanf("%lld%lld",&n,&k);
		for(int i=1; i<=n; i++)	scanf("%lld",&a[i]);
		int l = 1,r = 2e8,ans = 0;
		while(l <= r){
			int mid = (l + r)>>1;
			if(chk(mid)){
				ans = mid,l = mid + 1;
			}
			else	r = mid - 1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

D.More Wrong

题目描述:

交互题。
有一个长度为 \(n\) 的排列 \(p\)。
我们可以进行询问操作 \([l,r]\) 交互库会返回 \(p_l,p_{l+1},...,p_r\) 的逆序对个数,且这次操作的花费为 \((r-l)^2\)。
要求花费不超过 \(5 \cdot n^2\),找到最大值的位置。

题目分析:

考虑最大值在逆序对里满足什么性质。
就是最大值在结尾时不会对逆序对产生贡献,也就是设 \([l,r]\) 的最大值为 \(p_r\),则 \([l,r]\) 的逆序对数等于 \([l,r-1]\) 的逆序对数。
考虑这个 \(5 \cdot n^2\) 很类似归并排序最后的花费,也就是对于一个区间 \([l,r]\) 询问 \([l,mid]\) 和 \([mid+1,r]\) 的最大值的位置,然后根据最大值的性质就可以判断区间 \([l,r]\) 的最大值的位置。
根据等比数列求和这样的花费大概是 \(4\cdot n^2\) 的可以过。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int query(int l,int r){
	if(l == r)	return 0;
	cout<<'?'<<' '<<l<<' '<<r<<endl;cout.flush();
	int tmp;cin>>tmp;
	return tmp;
}
int solve(int l,int r){
	if(l == r)	return l;
	int mid = (l + r)>>1;
	int pos1 = solve(l,mid);
	int pos2 = solve(mid+1,r);
	if(pos1 > pos2)	swap(pos1,pos2);
	int ans1 = query(pos1,pos2);
	int ans2 = query(pos1,pos2-1);
	if(ans1 == ans2)	return pos2;
	return pos1;
}
signed main(){
	int T;cin>>T;
	while(T--){
		int n;cin>>n;
		int pos = solve(1,n);
		cout<<'!'<<' '<<pos<<endl;cout.flush();
	}
	return 0;
}

E.PermuTree

题目描述:

给定一个 \(n\) 个节点的树,你要对每个点给定一个点权 \(a_i\),要满足 \(a\) 为一个排列。
点对 \((x,y)\) 有贡献,当且仅当 \(a_x < a_{lca(x,y)} < a_y\),询问最大贡献和是多少。

题目分析:

对于根节点,我们显然可以给它的子树定一个大小关系,假设 \(1 < 2\) 也就是说子树 \(1\) 内的最大值小于子树 \(2\) 内的最小值。
可以发现这样定完之后,在这个点的贡献就相当于两个子树大小的乘积就很好算了。
考虑如果不满足这个条件,那么分讨一下发现将不满足条件的交换回去答案一定不劣。
因为我们存在 \(a_{lca(x,y)}\) 必须位于两个子树的权值之间,所以我们本质上就是将子树划分为两个集合,造成的贡献就是两个集合的大小乘积,要让乘积最大肯定就是两个集合的大小尽可能接近。
这个东西任何的贪心都是假的,只能 \(dp\),所以直接暴力背包复杂度就是 \(O(n^2)\) 就可以通过 easy version

标签:890,CF1856,return,int,题解,最大值,mid,long,sum
From: https://www.cnblogs.com/linyihdfj/p/17609918.html

相关文章

  • 【题解】Luogu[P9504] 『MGOI』Simple Round I C. 魔法禁林
    Link这题我们发现如果直接去枚举生命和法力值显然是不行的,又看到说最小的生命值,不禁想到最短路,但是怎么跑?我们令经过一条边之前魔力值为\(k\),那么该边的边权为\(\lfloor\dfrac{w}{k}\rfloor\),于是我们讲题目转化为了边权为\(\lfloor\dfrac{w}{k}\rfloor\)时\(s\)到\(t\)......
  • CF1856B
    原题翻译引理1:在\([l,r]\)内一定存在一个数\(x\)使满足\((r-l+1)|x\)证明:设\(k=r-l+1\),则\([l,r]\)内所有数都可以写成\(pk+q(0\leqq<k)\)的形式,且一定互不相同。根据抽屉原理即可证得结论。知道这个结论后我们就可以发现对于区间\([l,r]\)的某个整数,都可以在\([1,x]\)内......
  • [ABC310] D~F 题解
    [ABC310]D~F题解D-PeacefulTeams暴力搜索,搜索每个人在的队伍,为了去重,在一个人第一次加入新的队伍之后跳出。bitset<N>st;voiddfs(intu){for(inti=1;i<=m;i++)if(pos[a[i]]&&pos[b[i]]&&pos[a[i]]==pos[b[i]])return;if(u>n)......
  • Codeforces Round 890 (Div. 2)
    TalesofaSort题解找到最大的能够产生逆序对的数即可暴力\(O(n^2)\)枚举即可constintN=2e5+10,M=4e5+10;intn;inta[N];voidsolve(){cin>>n;intans=0;for(inti=1;i<=n;++i){cin>>a[i];}fo......
  • C++动态规划经典试题解析之打家劫舍系列
    1.前言力扣上有几道与打家劫舍相关的题目,算是学习动态规划时常被提及的经典试题,很有代表性,常在因内大大小小的社区内看到众人对此类问题的讨论。学习最好的方式便是归纳总结、借鉴消化,基于这个目的,本文对此类问题也做了讲解,在一些优秀思想的基础上添加了个人观点。闲话少说,进入......
  • Codeforces Round 890 (Div. 2) supported by Constructor Institute ————C - T
    关于这场div2,只能说一言难尽C题可以二分的,赛时看到n<=1000,直接往\(O(n^2)\)考虑,想了一会贪心的话能写出来,但是,细节太多没调出来,G掉打分。\(O(n^2)\)做法:思路:每次让i为起点,往前贪心枚举,并且当前位置如果满足,也要枚举当前区间,细节就是要注意上下限,赛时,漏了一种上界小于下届的情......
  • P9499 「RiOI-2」change 题解--zhengjun
    思维妙妙题。赛时的错误做法:找到每个点往后进位变优的位置,最多\(O(\log)\)个;然后从前往后能变优就变优,往后贪心进位。hack数据:0133510021022输出:100这样子由于\(1\)到\(2\)不优,而\(1\)到\(3\)个数不够,所以导致无法进位到\(3\)。所以加强一下......
  • CentOS8安装Docker报错问题解决
    问题描述CentOS版本:8.5.2111。#cat/etc/redhat-releaseCentOSLinuxrelease8.5.2111安装准备:#安装所需软件包sudoyuminstall-yyum-utilsdevice-mapper-persistent-datalvm2#设置docker仓库:推荐阿里云sudoyum-config-manager--add-repohttp://mirrors.al......
  • Codeforces Round 890 (Div. 2) supported by Constructor Institute 题解
    A.TalesofaSort关键就是找逆序对记一组逆序对下标为\(l,r\),则求出最大的\(a_l\)即可B.GoodArrays记要构造的GoodArray为\(b\)前置:\(\forall1\lei\len,b_i=1\)然后\(O(n)\)扫一遍看一下有没有重复,有重复就\(b_i\leftarrowb_i+1\)扫完之后,记\(sum=\sum_......
  • Codeforces Round 882 (Div. 2) 题解
    A.TheManwhobecameaGod求出相邻两个元素的差值,去掉前\(m\)个大的差值以后的差值和即为答案B.HamonOdyssey由按位与的性质可以知道,前缀与和的值只会越来越小,只要和为\(0\)的时候我们就清空按位与前缀和,增加一下次数,如果最终次数不为\(0\),特判一下,次数加一即可C.......