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

A题链接

题目大意:
有一个操作,把 a i a_i ai 换成 a j a_j aj
n n n k k k 和序列 a i a_i ai,求前 k k k 个值的和的最小值的时候,最小的操作次数。

题解思路:
贪心地想,因为 a i a_i ai 是个排列(1-n),所以前 k k k 个值的和的最小值是1-k,那么就检查一下,前 k k k 个数中,有多少个数字不是1-k之中的就可以了

代码如下:

#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;
 
const int N=1100;
int a[N];
 
void solve()
{
	int n,k;
	cin>>n>>k;
	rep(i,1,n)
		cin>>a[i];
	
	map<int,int> mp;
	rep(i,1,k)
		mp[a[i]]++;
	
	int ans=0;
	rep(i,1,k)
		if(!mp[i])
			ans++;
	cout<<ans<<endl; 
}

B题链接

题目大意:
给一个n,求 ∑ i = 1 n l c m ( i , a i ) \sum_{i=1}^nlcm(i,a_i) i=1nlcm(i,ai)最大时,ai的排列

解题思路:
规律:倒着开始枚举,交换相邻两个数字就可以了,变成了
奇数: 1 , 3 , 2 , 5 , 4.... n , n − 1 1,3,2,5,4....n,n-1 1,3,2,5,4....n,n1
偶数: 2 , 1 , 4 , 3 , 6 , 5... n , n − 1 2,1,4,3,6,5...n,n-1 2,1,4,3,6,5...n,n1

打表法:通过手动模拟10个数字的 a i a_i ai ,发现规律(我就是这样做的)

不是很完善的证明:
首先,对于 l c m ( a , b ) lcm(a,b) lcmab来说,最优的情况是 l c m ( a , b ) = a × b lcm(a,b)=a×b lcmab=a×b
其次, l c m ( n , n − 1 ) = n × ( n − 1 ) lcm(n,n-1)=n×(n-1) lcmnn1=n×n1
所以,对于任意 a i a_i ai 的组合来讲,最优的解是指交换相邻的两个数字即可
因为,当下, n × ( n − 1 ) n×(n-1) n×(n1) 必定大于任何其他n*m的组合。当 n → ∞ {n\to\infty} n时,和的上界对最大的 l c m lcm lcm 的大小有很大的关系,所以 n × ( n − 1 ) n×(n-1) n×(n1) 绝对是当下最优的解,同理 ( n − 2 ) × ( n − 2 ) (n-2)×(n-2) (n2)×(n2)。。。。

代码如下:

#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;
 
const int N=2e5+10;
int a[N];
 
void solve()
{
	int n;
	cin>>n;
	rep(i,1,n)
		a[i]=i;
	
	for(int i=n;i>1;i-=2)
		swap(a[i],a[i-1]);
	rep(i,1,n)
		cout<<a[i]<<" ";
	cout<<endl;
}

C题链接

题目大意:
一个操作:选一个数字 x x x,然后让所有等于 x x x a i a_i ai 都变成 0 0 0
n n n 和序列 a i a_i ai ,求让序列变成非递减的有序序列的最小操作次数

解题思路:
首先,需要两样东西,
①每一种数字最后一次出现的坐标(用map存一下就行)
②精确地知道在某一个坐标及之前,有多少个不同的数字(用前缀和维护一下就行)
接下来只要确定,最后一个有递减趋势的位置的数字最后一次出现的位置的坐标就可以了
然后从后往前枚举,找到最后一个有递减趋势的坐标 i d x idx idx,然后把 i d x idx idx 及之前的所有数字全部改成0就可以了(就是输出idx及之前有多少个不一样的数字)

代码如下:

#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;
 
const int N=1e5+10;
int a[N];
int s[N],st[N];
 
 
void solve()
{
	int n;
	cin>>n;
	rep(i,1,n)
	{
		cin>>a[i];
		s[i]=st[i]=0;
	}
		
	
	map<int,int> idx;
	rep(i,1,n)
	{
		idx[a[i]]=max(idx[a[i]],i);
		s[i]=s[i-1];
		if(mp[a[i]]==0) s[i]++;
	}
	int k=0;
	rep(i,1,n)
	{
		if(a[i]<a[i-1])
		{
			st[k]++;
		}
		k=max(idx[a[i]],k);
	}
		
			
	
	dec(i,n,1)
		if(st[i])
		{
			cout<<s[i]<<endl;
			return;
		}
	cout<<0<<endl;
}

D题链接

题目大意:
一个操作:可以把一条边的值修改为任意数字
n n n k k k 还有序列 a i a_i ai ,他们一次连成一个圈
d(l,r)表示l到r的最小的边的长度,如果l到r直连并不是最优解,就可以l到1,然后1到r这样走
问修改k次后,最长的d(l,r)是多少

解题思路:
首先,贪心地思考,就可以得出下式 d ( l , r ) = m i n ( d ( l , r ), 2 ∗ d ( 1 , l − 1 ), 2 ∗ d ( r + 2 , n )) d(l,r)=min(d(l,r),2*d(1,l-1),2*d(r+2,n)) dl,r=mindlr),2d1l1),2dr+2n))这样能求出来最优解
由此,可以二分答案
2 × a i < m i d 2×a_i<mid 2×aimid 的时候,就需要修改,记录一下,求个前缀和,求个后缀和,表示修改次数
如果 a i a_i ai 小于 m i d mid mid ,就修改, a i + 1 < m i d a_i+1<mid ai+1<mid,就需要修改。
修改保证剩余边最小值足够大就可以了,所以直接把需要修改的边修改为 1 0 9 10^9 109 就可以

代码如下:

#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;
 
const int N=1e5+10;
int a[N];
int s[N],st[N];
int n,m;
 
bool check(int mid)
{
	int s[N],f[N];
	s[0]=f[n+1]=0;
	rep(i,1,n)
	{
		s[i]=s[i-1];
		if(2*a[i]<mid)
			s[i]++;
	}
	dec(i,n,1)
	{
		f[i]=f[i+1];
		if(2*a[i]<mid)
			f[i]++;
	}
	
	rep(i,2,n)
	{
		int k=0;
		if(a[i]<mid) k++;
		if(a[i-1]<mid) k++;
		if(s[i-2]+f[i+1]+k<=m)
			return 1;
	}
	
	dec(i,n-1,1)
	{
		int k=0;
		if(a[i]<mid) k++;
		if(a[i+1]<mid) k++;
		if(k+s[i-1]+f[i+2]<=m)
			return 1;
	}
	
	return false;
}
 
void solve()
{
	cout<<endl;
	cin>>n>>m;
	rep(i,1,n)
		cin>>a[i];
	
	int l=1,r=1000000000;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(check(mid))
			l=mid;
		else r=mid-1;
	}
	cout<<l<<endl;
}

我只能说,这次生病给我病麻了,发烧了得四五天,打退烧针都不管用,后来吊了五天吊瓶,现在咳嗽还没好,头疼+头晕。
本来不想打的,但是想了想还是要训练嘛,就打了小号,结果出奇的把D给a掉了,一开始想着是图论,我图论知识基本为零,想了想发现是个二分,写完就开始找bug,找了半小时bug,忘记反着过一遍了,改了之后压线过的。
没想到啊,复健第一场,打大号都上分了,我打的小号。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值