【题解】Codeforces Round 890(CF1856)

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

A.Tales of a Sort

题目描述:

给定长度为 n 的序列 a,每次操作为对于所有 iai 变为 max(ai1,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 满足:

  • i[1,n] aibi
  • i=1nai=i=1nbi

题目分析:

考虑调整,一定是一些数变大另一些数变小。
所以直接将所有 ai=1 的位置的 bi 设为 2 其余部分的 bi 设为 1,显然这一定是一个和最小的方案。
如果此时 inbi>inai,那么也就无解了。
否则如果 inbiinai,就可以通过调整 ai=1 的位置对应的 bi 的值使得和满足条件。

代码:

点击查看代码
#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[1,n1] 满足 aiai+1,将 ai1

询问进行操作后的最大的 maxi=1nai 的值。
n1000,k108

题目分析:

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

代码:

点击查看代码
#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] 交互库会返回 pl,pl+1,...,pr 的逆序对个数,且这次操作的花费为 (rl)2
要求花费不超过 5n2,找到最大值的位置。

题目分析:

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

代码:

点击查看代码
#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 个节点的树,你要对每个点给定一个点权 ai,要满足 a 为一个排列。
点对 (x,y) 有贡献,当且仅当 ax<alca(x,y)<ay,询问最大贡献和是多少。

题目分析:

对于根节点,我们显然可以给它的子树定一个大小关系,假设 1<2 也就是说子树 1 内的最大值小于子树 2 内的最小值。
可以发现这样定完之后,在这个点的贡献就相当于两个子树大小的乘积就很好算了。
考虑如果不满足这个条件,那么分讨一下发现将不满足条件的交换回去答案一定不劣。
因为我们存在 alca(x,y) 必须位于两个子树的权值之间,所以我们本质上就是将子树划分为两个集合,造成的贡献就是两个集合的大小乘积,要让乘积最大肯定就是两个集合的大小尽可能接近。
这个东西任何的贪心都是假的,只能 dp,所以直接暴力背包复杂度就是 O(n2) 就可以通过 easy version
考虑优化就是对于点 uvsonuszvn,所以其本质不同的 szi 只有 O(n) 个,这样就可以将这个问题转化为多重背包,这样的好处就是可以将物品数量优化到 O(m) 级别,那么我们使用多重背包的经典优化二进制分组 + bitset 这个题就可以做到 O(nnlognw) 的复杂度,实现的好一些加一些剪枝之类的就可以轻松跑过了。

posted @   linyihdfj  阅读(81)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示