Codeforces Round #838 (Div. 2) 题解

A 题目链接:Problem - A - Codeforces

input:

4
4
1 1 1 1
2
7 4
3
1 2 4
1
15

output:

0
2
1
4

题意:

现给出一个定义:如果一个数组所有元素的和是偶数,则称该数组为 good 。

给定长度为 n 的数组 a ,给定一种操作:任选数组 a 中的一个元素,将其除以2(下取整),问最少操作多少次,可以使数组 a 为 good。

思路:

数组中奇数的个数会影响总和的结果:

1.如果奇数个数为偶,那么不需要任何操作,数组 a 即为 good,直接输出0即可。

2.如果奇数个数为奇,那么需要将奇数个数变为偶。有两种方案,一是令某个奇数变为偶数,二是令某个偶数变为奇数。由于 n 很小,所以说直接遍历数组,维护每个元素奇偶性改变所需要的最小操作次数的最小值即可。

代码如下:

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=2e5+10;

int t,n;
int a[N];
bool vis[N];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--)
	{
		cin>>n;
		int cnt=0;
		
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			vis[i]=0;
			if(a[i]&1)
			{
				cnt++;
				vis[i]=1;
			}
		}
		if(cnt%2==0)
		{
			cout<<0<<endl;
			continue;
		}
		int ans=10000;
		for(int i=1;i<=n;i++)
		{
			int k=0;
			if(vis[i])
			{
				while(a[i]%2==1)
				{
					a[i]/=2;
					++k;
				}
				ans=min(ans,k);
			}
			else
			{
				while(a[i]%2==0)
				{
					a[i]/=2;
					++k;
				}
				ans=min(ans,k);				
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

B 题目链接:Problem - B - Codeforces

input:

4
4
2 3 5 5
2
4 8
5
3 4 343 5 6
3
31 5 17

output:

4
1 2
1 1
2 2
3 0
0
5
1 3
1 4
2 1
5 4
3 7
3
1 29
2 5
3 3

题意:

现给出一个定义:如果一个数组 a 的所有元素对 ai , aj 满足 max(ai , aj) 可以被 min(ai , aj) 整除,则称该数组为 good 。

给定长度为 n 的数组 a ,给定一种操作:任选 a 中的一个元素,令其加上不大于自身的任意自然数 x ,并保证元素修改后的值不大于1e18。试对数组 a 进行修改,使其为 good ,并保证操作次数不大于 n 次。

思路:

直接对数组 a 从小到大排序,以最小元素的两倍为基准,将其后不大于基准值的元素全部构造为基准值,若当前元素已经大于该元素的两倍,将基准值不断乘2,直至基准值大于当前元素,然后构造,依次类推,注意计数并离线处理答案。

代码如下:

#pragma GCC optimize(3)
#include<bits/stdc++.h>
//#define endl "\n"
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int mod=1e9+7;
const int N=2e5+10;


int t,n;

struct node{
	ll v;
	int id;
}a[N];

bool cmp(node a,node b)
{
	return a.v<b.v;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--)
	{
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i].v;
			a[i].id=i;
		}
		sort(a+1,a+n+1,cmp);
		ll cur=a[1].v;
		vector<PII> vec;
		for(int i=2;i<=n;i++)
		{
			while(a[i].v>cur*2)
			{
				cur*=2;
			}
			if(a[i].v<cur*2)
			{
				vec.push_back({a[i].id,cur*2-a[i].v});
			}
			else if(a[i].v==cur*2)
			{
				continue;
			}
		}
		cout<<vec.size()<<endl;
		for(int i=0;i<vec.size();i++)
		{
			cout<<vec[i].first<<" "<<vec[i].second<<endl;
		}
	}
	return 0;
}

C 题目链接:Problem - C - Codeforces

input:

6
1
1
1
0
2
11
3
010
9
101101111
37
1011011111011010000011011111111011111

output:

1
1
3
3
21
365

题意:

现给出一个定义:对于长度为奇数01串 b ,如果对于每一个奇数位的前缀,都满足该前缀的中位数即为该前缀的最后一位,那么称数组 b 为 good(01串的中位数即为众数)。

对于一个长度为 k 的01串,定义其 extension:长度为 2k - 1 的01串,第 2i - 1 位是原串的第 i 位,构造方式有2^(k-1)种(可以理解为长度k的串,两两字符之间共 k - 1 个空,每个空可以插入0或1)。

给定长度为 n 的01串 s ,试求出该串的所有前缀的所有满足 good 的扩展方案总和(有点绕,大致就是这个意思,如果不能很好理解我的翻译,可以看原题面)。

思路:

明显递推。推了一会儿状态转移,只推出了当前位和前一位相等的状态转移方程,看了一下样例,直接根据样例猜的当前位和前一位不相等的状态转移:

1.当前位和前一位相等,则当前前缀的目标方案数是前一位的前缀的目标方案数的两倍

2.当前位和前一位不等,则当前前缀的目标方案数为1

代码如下:

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=2e5+10;

int t,n;
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--)
	{
		cin>>n;
		string s;
		cin>>s;
		s=" "+s;
		int cur=1;
		int ans=1;
		for(int i=2;i<=n;i++)
		{
			if(s[i]!=s[i-1])
			{
				cur=1;
				ans=(ans+1)%mod;
			}
			else
			{
				cur=(cur*2)%mod;
				ans=(ans+cur)%mod;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

D 题目链接:Problem - D - Codeforces

input:

2
2

1

1
5

2

4

1

output:



? 1 2

! 1 2


? 1 2

? 2 3

! 3 3

题意:

交互题。

给定一个数 n ,评测方有一个长度为 n 的排列 p(由 0 ~ n - 1 组成,询问方看不到该排列)。现在可以向评测方提出不超过 2n 次询问,询问格式为:"? i j",评测方会给出一个数,代表 gcd( pi , pj)。所有询问结束后,询问方需提供两个下标,意为数字 0 在两个下标之一上,提供格式为:"! x y"

提供答案结束后,评测放会返回 1 或 -1,询问方须接收这个返回。1代表答案正确,-1代表答案错误,程序返回 wrong answer。

注意:gcd(0 , x) == x (这个很重要!!!)

思路:

某一个非0值与 0 gcd,得到的是该数本身,并且该数与其余任何数的 gcd 都不可能超过该数本身。所以0和其余数 gcd 得到的值是这个数通过 gcd 能得到的值里最大的。因此我们令 x = 1 , y = 2,从下标3开始往后遍历,每次遍历询问两次: x, i 和 y , i,设得到的两个结果分别为 a 和 b 。分以下三种情况讨论:

1. a == b,说明当前 pi 一定不是 0 ,因为0和两个不同的数 gcd 一定是得到不同的值,所以直接跳过,不做任何操作,继续遍历

2. a > b,我们维护 gcd 较大的情况,令 y = i ,继续遍历

3. a < b,意义同上,令 x = i ,继续遍历

遍历结束后提供 x 和 y 即可。

代码如下:

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=2e5+10;

int t,n,x,y,a,b;
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		x=1,y=2;
		for(int i=3;i<=n;i++)
		{
			cout<<"? "<<x<<" "<<i<<endl;
			cin>>a;
			cout<<"? "<<y<<" "<<i<<endl;
			cin>>b;
			if(a==b)
				continue;
			else if(a>b)
				y=i;
			else 
				x=i;				
		}
		int k;
		cout<<"! "<<x<<" "<<y<<endl;
		cin>>k; 
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值