Codeforces Round #830 (Div. 2) (A、B、C、D1)

本文介绍了Codeforces平台上的编程竞赛中,如何解决A、B、C1/C2和D1等题目。A题关注最小代价使数组元素互质,B题涉及字符串的单调不减变换,C题讨论了最大化进位计数的问题,而D1题则是一个关于集合操作和查询的题目。文章展示了不同的解题思路和代码实现,强调了问题分析和算法优化的重要性。
摘要由CSDN通过智能技术生成

A题

Problem - A - CodeforcesCodeforces. Programming competitions and contests, programming communityhttps://codeforces.com/contest/1732/problem/A题意:
        给个数组,每次可以将某个不同位置的值a[i] 变为 gcd(a[i], i),需要花费n - i + 1,求得到全部数组gcd等于1的最小代价

思路:
        可以观察出我们只需要分析最后两位即可,首先是因为相邻两数互质,其次是因为n - i + 1的代价限制使得我们选择后两个更为划算。那么直接分析每一个变为gcd(a[i], i)后的值,选满足条件的最小代价即可。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long

using namespace std;

const int N = 30;

int T;
int a[N];

int gcd(int x, int y)
{
	return y > 0 ? gcd(y, x % y) : x;
}

signed main()
{
	scanf("%d", &T);
	int n;
	while(T--)
	{
		bool st = false;
		scanf("%d", &n);
		int g = 0;
		for (int i = 1; i <= n; ++ i) {
			scanf("%d", &a[i]);
			if(i == 1) g = a[i];
			else g = gcd(g, a[i]);
			if(g == 1) st = true;
		}
		
		
		if(st) printf("%d\n", 0);
		else {
		    if(n == 1){
		        printf("%d\n", 1);
		        continue;
		    }
			int sn1 = gcd(a[n], n), sn2 = gcd(a[n - 1], n - 1);
			for (int i = n - 1; i >= 1; -- i) {
			    sn1 = gcd(sn1, a[i]);
			    if(sn1 == 1) {
			        st = true;
			        break;
			    }
			}
			if(st) printf("%d\n", 1);
			else {
			    for (int i = n; i >= 1; -- i)
			    {
			        if(i == n - 1) continue;
			        else sn2 = gcd(sn2, a[i]);
			        if(sn2 == 1){
			            st = true;
			            break;
			        }
			    }
			    if(st) printf("%d\n", 2);
			    else printf("%d\n", 3);
			}
		}
	}
	return 0;
}

B题

Problem - B - CodeforcesCodeforces. Programming competitions and contests, programming communityhttps://codeforces.com/contest/1732/problem/B题意:
        本题是给个只有 '0' 和 ‘1’ 的字符串,要求我们最终经过操作使得整个字符串单调不减,我们可以进行的操作是每次选择一个位置,使得其以及后面的所有数,变1为0,变0为1,问最少需要多少次操作。

思路:

        根据观察我们发现以下结论:

        int ans = 0;

  •   如果字符串末端为 '0',那么我们只需要将 ans = 1 ,然后如果遇到 '0' 前面的 '1' ,就ans += 2
  •   如果字符串末端为 '1',那么我们只需要做:如果遇到 '0' 前面的 '1' ,就ans += 2

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 1e5 + 10;

int T;
char a[N];

signed main()
{
	scanf("%d", &T);
	int n;
	while(T--)
	{
		scanf("%d", &n);
		scanf("%s", a);
		bool fis = true;
		int ans = 0, is = a[n - 1] - '0';

		if(is){
			for (int i = n - 2; i > 0; -- i) if(a[i] == '0' && a[i - 1] == '1') ans += 2;
		}else{
			for (int i = n - 2; i > 0; -- i) {
				if(fis && a[i] == '1') ans = 1, fis = false;
				if(!fis){
					if(a[i] == '0' && a[i - 1] == '1') ans += 2;	
				}
			}
			if(ans == 0) if(a[0] == '1') ans = 1;
		}
		printf("%d\n", ans);
	}
	return 0;
}

C1 \ C2

Problem - C1 - CodeforcesCodeforces. Programming competitions and contests, programming communityhttps://codeforces.com/contest/1732/problem/C1题意:
        给一个数组,给一段区间(Hard 是 n段),在这个区间 L -> R内,找到使得f(l,r)=sum(l,r)−xor(l,r)最大且区间宽度最小的情况。

思路:

        明确,maxf(l,r)=f(L,R), f(l,r) 的意义是二进制加法时进位的总数

        所以很明显二段性就出来了,固定左端点,右端点二分,朴素复杂度 O(qNlog⁡N) ,会 T

        脑筋急转弯一下,为了保证进位总数不变,那么最多去掉31个大于0的数,本题恶心就恶心在数组中有0,所以需预处理个 nxt 数组,时间复杂度 O(31qlog⁡N) ,注意会爆int

代码:

#include<bits/stdc++.h>
 
using namespace std;
 
#define rep(i,x,y)  for(i=x;i<=y;++i)
#define repf(i,x,y) for(i=x;i>=y;--i)
#define LL long long 
#define inf 0x3f3f3f3f

const int N=1e5+10;
int t,n,q,L,R;

LL a[N],sum[N],xr[N];
int nxt[N];

int main()
{
    int i,j,bg,ans,ll,rr,ttp;
    
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&q);
        rep(i,1,n)  scanf("%lld",&a[i]);
        rep(i,1,n)  sum[i]=sum[i-1]+a[i],xr[i]=xr[i-1]^a[i];
        nxt[n]=inf;
        repf(i,n-1,1)
        {
            if(a[i+1]>0)    nxt[i]=i+1;
            else nxt[i]=nxt[i+1]; 
        }
        
        while(q--)
        {
            scanf("%d%d",&L,&R);
            LL tar=sum[R]-sum[L-1]-(xr[R]^xr[L-1]);
            bg=L,ttp=31,ans=R-L+1,ll=L,rr=R;
            
            while(bg<=R)
            {
                if(sum[R]-sum[bg-1]-(xr[R]^xr[bg-1])<tar)   break;
                int left=bg,right=R,mid;
                while(left<=right)
                {
                    mid=left+right>>1;
                    if(sum[mid]-sum[bg-1]-(xr[mid]^xr[bg-1])==tar)  right=mid-1;
                    else left=mid+1;
                }
                if(ans>left-bg+1)   ans=left-bg+1,ll=bg,rr=left;
                
                if(a[bg]>0)
                {
                    if(!ttp)  break;
                    ttp--;
                }
                bg=nxt[bg];
                if(bg==inf) break;
            }
            printf("%d %d\n",ll,rr);
        }
    } 
    
    return 0;   
} 

D1题

Problem - D1 - CodeforcesCodeforces. Programming competitions and contests, programming communityhttps://codeforces.com/contest/1732/problem/D1题意:
        初始的set里只有元素0,若干个操作可以往set里添加元素但是不会减少元素。查询操作是求出第一个不出现在set里的k的倍数。

思路:
        直接记忆化搜索,如果有了就输出+本身,否则直接输出本身,同时存下来每次添加的元素。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>

using namespace::std;

typedef long long  ll;

int n;
char s[10];
ll lk;
map<ll,ll>q;
map<ll,ll>w;

int main(){
    cin>>n;
    
    while (n--) {
        cin>>s>>lk;
        if (s[0]=='+') {
            q[lk]=1;
        }
        else{
            if (!q[lk]) {
                printf("%lld\n",lk);
            }
            else{
                if (w[lk]==0) {
                    for (ll i = lk; ; i+=lk) {
                        if (q[i]==0) {
                            printf("%lld\n",i);
                            w[lk]=i;
                            break;
                        }
                    }
                }
                else{
                    for (ll i = w[lk]; ; i+=lk) {
                        if (q[i]==0) {
                            printf("%lld\n",i);
                            w[lk]=i;
                            break;
                        }
                    }
                }
            }
        }
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dawpro_加薪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值