Codeforces Round #830 (Div.2) 题解

A 题目链接:Problem - A - Codeforces

 input:
 

9
1
1
1
2
2
2 4
3
3 6 9
4
5 10 15 20
5
120 60 80 40 80
6
150 90 180 120 60 30
6
2 4 6 9 12 18
6
30 60 90 120 125 125

output:

0
1
2
2
1
3
3
0
1

题意:给长度为n的数组a,给定一种操作,可以使任意 ai = gcd(ai,i),代价是n-i+1,问最少操作几次可以使整个数组的gcd为1

思路:数据量很小,直接对数组gcd一遍,如果为1,直接输出0.如果不满足,直接看后两个数变化后能否满足条件。只操作an,只操作an-1,和二者都操作的代价分别为1,2,3,暴力看一下哪个满足条件,输出最小的。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=25;
int t,n;
int a[N];
 
bool gcd()
{
	int k=a[n];
	for(int i=n-1;i>=1;i--)
	{
		k=__gcd(k,a[i]);
		if(k==1)
			return true;
	}
	if(k==1)
		return true;
	return false;
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>t;
	while(t--)
	{
		cin>>n;
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		}
		if(gcd())
		{
			cout<<ans<<endl;
			continue;
		}
		else
		{
			int an=a[n];
			a[n]=__gcd(a[n],n);
			if(gcd())
			{
				cout<<1<<endl;
				continue;
			}
			else
			{
				a[n]=an;
				a[n-1]=__gcd(a[n-1],n-1);
				if(gcd())
				{
					cout<<2<<endl;
					continue;
				}
				else
				{
					cout<<3<<endl;
					continue;
				}
			}
		}
	}	
	return 0;
}

B 题目链接:https://codeforces.com/contest/1732/problem/B

input:

8
1
1
2
10
3
101
4
1100
5
11001
6
100010
10
0000110000
7
0101010

 output:

0
1
2
1
2
3
1
5

题意:给长度为n的01串,给定一种操作,选定一个下表i,令所有下标不小于i的元素0变1,1变0,问最少操作多少次可以使序列变为不下降序列

思路:从序列中找到第一个1,然后此后的若干0,若干1为一个块,输出块的数量-1即可。因为第一个“1”块的后面必须全部变成1,才满足不下降,所以每一个块都需要付出一个代价

代码如下

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int t,n;
int main()
{
	ios::sync_with_stdio(false);
	cin>>t;
	while(t--)
	{
		string s;
		cin>>n;
		cin>>s;
		int cnt=0;
		int i;
		for(i=0;i<n;i++)
		{
			if(s[i]=='1')
				break;
		}
		i++;
		for(i;i<n;i++)
		{
			if(s[i]!=s[i-1])
				cnt++;
		}
		cout<<cnt<<endl;
	}
	
	return 0;
}

C1,C2没做出来(枯了)明天补

D 题目链接:Problem - D1 - Codeforces

input:

15
+ 1
+ 2
? 1
+ 4
? 2
+ 6
? 3
+ 7
+ 8
? 1
? 2
+ 5
? 1
+ 1000000000000000000
? 1000000000000000000

output:

3
6
3
3
10
3
2000000000000000000

题意:给一个集合,最初只有一个0,每次给出两种操作:1.向集合中加入一个x(保证可以加入)2. 给一个k,问最小的能整除k的,且不在集合内的数是多少

思路:数大,注意开long long。这道题我们开了两个map,map1用来存集合,map2用来存k的最近一次询问的结果是多少。对于每一次询问k,先看一看map2里有没有记录,如果有的话,这一次可以从map2[k]开始找,就令k=map2[k]。然后k每次增长k,看集合里有没有这个值,没有的话直接输出。

代码如下

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int t;
map<ll,ll> mp1,mp2;
int main()
{
	ios::sync_with_stdio(false);
	cin>>t;
	while(t--)
	{
		char c;
		cin>>c;
		ll k;
		cin>>k;
		if(c=='+')
		{
			mp1[k]=1;
			continue;
		}
		else
		{
			ll i;
			if(mp2.count(k)==0)
				i=k;
			else
				i=mp2[k];
			for(i;;i+=k)
			{
				if(mp1.count(i)==0)
				{
					cout<<i<<endl;
					break;
				}
			}
			mp2[k]=i;
		}
	}
	
	return 0;
}

D2 题目链接:Problem - D2 - Codeforces

input:

18
+ 1
+ 2
? 1
+ 4
? 2
+ 6
? 3
+ 7
+ 8
? 1
? 2
+ 5
? 1
+ 1000000000000000000
? 1000000000000000000
- 4
? 1
? 2

 output:

3
6
3
3
10
3
2000000000000000000
3
4

题意:上一题的困难版本,加入了一种操作‘-‘:选定集合中一个数,将其删除,其余均一样

思路:rank的时候没做出来,时间都花在c1上了,感谢@与风做友同学的强大思路。在上一题map1,map2基础上增加一个map3,用来存储哪些数被删过。在“+”操作中,如果加入进来的数曾经被删,也就是在map3中有记录,那就将其在map3中删除。在“-”操作中,将该数在map1中删除,并加入到map3中。查询时,如果map2中没有记录,就直接从k开始遍历,操作同D1.如果有记录,那么看map3,先找到第一个map3中不小于k的值,然后开始遍历,从删的数里面直接找满足条件的数,找到就输出,如果比map2[t]大了,就不如直接从map2[t]开始找。然后找到数以后输出

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+1;
typedef long long ll;
int T;
long long t;
map<ll,ll> mp1,mp2,mp3;
int main()
{
	ios::sync_with_stdio(false);
	cin>>T;
	char op;
	while(T--)
	{
		cin>>op>>t;
		if(op=='+')
		{
			mp1[t]=1;
			if(mp3.count(t)!=0) mp3.erase(t);
		}
		else if(op=='-')
		{
			mp1.erase(t);
			mp3[t]=1;
		}
		else
		{
			ll i;
			if(mp2.count(t)==0)
			{
				for(i=t;;i+=t)
				{
					if(mp1.count(i)==0)
					{
						cout<<i<<endl;
						break;
					}
				}
				mp2[t]=i;
			}
			else
			{
				bool f=0;
				for(auto iter=mp3.lower_bound(t);iter != mp3.end(); iter++)
			    {
			    	if(iter->first>mp2[t]) break;
			    	if((iter->first)%t==0)
			    	{
			    		f=1;
			    		cout<<iter->first<<endl;
			    		break;
					}
				}
				
				if(f) continue;
				for(i=mp2[t];;i+=t)
				{
					if(mp1.count(i)==0)
					{
						cout<<i<<endl;
						break;
					}
				}
				mp2[t]=i;
			}
		}
	}
	return 0;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值