Codeforces Round #830 (Div. 2)

A. Bestie

在这里插入图片描述

Sample 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

Sample output

0
1
2
2
1
3
3
0
1

题意:

给你一个长度为n的数组,你可以进行一种操作,将第i个数变为gcd(i,a[i]),这一步操作的花费为n-i+1,然后问你最少的操作花费将这个序列的gcd变成1。

思路:

因为这个n最大不超过20,将最后两个数都进行一步操作就能将整个序列的gcd变成1,这个花费是3,所以说只需要找那种只变一个数就能将gcd变成1的答案的最小值和这个3取最小就行,如果本来这个序列的gcd就是1的话那直接输出0,下面看代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
int a[22],b[22];
void solve(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    int x = a[1];
    for(int i=2;i<=n;i++) x = __gcd(x,a[i]);
    if(x == 1){
        puts("0");
        return;
    }
    int ans = n>=2?3:1;
    for(int i=n;i>=1;i--){
        memcpy(b,a,sizeof b);
        b[i] = __gcd(b[i],i);
        int x = b[1];
        for(int i=2;i<=n;i++) x = __gcd(x,b[i]);
        if(x == 1) ans = min(ans,n-i+1);
    }
    printf("%d\n",ans);
}
int main(){
    int _;
    scanf("%d",&_);
    while(_--) solve();
    return 0;
}

B. Ugu

在这里插入图片描述

Sample input

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

Sample output

0
1
2
1
2
3
1
5

题意:

给你一个长度为n的01串,你现在可以进行一步操作,选定一个位置i,将(i,n)这个序列中的所有元素都取反,现在问你最少需要多少步这种操作能将这个数组变成不降的

思路:

就是暴力,如果当前位是1但是后面有0的话那就翻转,实现方式的话就是记录一下反转了奇数次还是偶数次就行,下面看代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
char s[N];
int sum[N];
void solve(){
    int n,pos = 0;
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;i++){
        s[i] = s[i] - '0';
        sum[i] = sum[i-1] + s[i];
    }
    int cnt = 0,ans = 0;
    s[0] = 0;
    for(int i=1;i<=n;i++){
        if((s[i]^cnt) && ((!cnt && sum[n] - sum[i-1] != n - i + 1) || (cnt && sum[n] - sum[i-1] != 0))){
            ans ++;
            cnt ^= 1;
        }
    }
    printf("%d\n",ans);
}
int main(){
    int _;
    scanf("%d",&_);
    while(_--) solve();
    return 0;
}

C1. Sheikh (Easy version)

在这里插入图片描述

Sample input

6
1 1
0
1 1
2 1
5 10
1 2
3 1
0 2 4
1 3
4 1
0 12 8 3
1 4
5 1
21 32 32 32 10
1 5
7 1
0 1 0 1 0 1 0
1 7

Sample output

1 1
1 1
1 1
2 3
2 3
2 4

题意:

给你一个长度为n的区间,有q组询问,每组询问给你一个L,R,问在(L,R)内哪一段的区间和减去区间异或和最大,若有多个最大值则输出最短的那个区间,C1与C2的区别只是q=1,其中L1 = 1,R1 = n。

思路:

有一个性质,就是一段区间的加和一定小于等于一个区间的异或和,所以说一整个区间(1,n)的的差值就是最大的差值,最后左右两边向中间缩就行,这个过程可以用双指针来维护,下面请看代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200010;
int a[N];
long long sum1[N],sum2[N];
void solve(){
    int n,q;
    scanf("%lld%lld",&n,&q);
    int l,r;
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        sum1[i] = sum1[i-1] + a[i];
        sum2[i] = sum2[i-1] ^ a[i];
    }
    scanf("%lld%lld",&l,&r);
    int ans=0;
     ans=(sum1[r] - sum1[l-1] - (sum2[r] ^ sum2[l-1]));
    int j=1;
    int ansl=0;
    int ansr=1e9;
    for(int i=1;i<=n;i++)
    {
    	j=max(j,i);
        while(j<n && (sum1[j] - sum1[i-1] - (sum2[j] ^ sum2[i-1]))<ans)
            j++;
        if(j-i+1<ansr-ansl+1 && (sum1[j] - sum1[i-1] - (sum2[j] ^ sum2[i-1])==ans))
        {
            ansl=i;
            ansr=j;
        }
    }
    printf("%lld %lld\n",ansl,ansr);
}
signed main(){
    int _;
    scanf("%lld",&_);
    while(_--) solve();
    return 0;
}

D1. Balance (Easy version)

在这里插入图片描述

Sample input1

15

  • 1
  • 2
    ? 1
  • 4
    ? 2
  • 6
    ? 3
  • 7
  • 8
    ? 1
    ? 2
  • 5
    ? 1
  • 1000000000000000000
    ? 1000000000000000000

Sample output1

3
6
3
3
10
3
2000000000000000000

Sample input2

6

  • 100
    ? 100
  • 200
    ? 100
  • 50
    ? 50

Sample output2

200
300
150

题意:

一开始有一个空集合,给你n组询问,每一组都会进行+操作或者是?操作,分别是向集合内加一个数和询问x的最小没出现过的倍数是多少

思路:

如果暴力的来看,所有询问的暴力操作加起来就是nlogn的复杂度,因为对2的询问就是n/2,对3的询问是n/3,… ,所以说所有书的加起来就是n*ln(n),下面请看代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
void solve(){
    int m;
    map<long long,long long> mp,st;
    scanf("%d",&m);
    while(m--){
        long long t;
        char s[2];
        scanf("%s%lld",s,&t);
        if(*s == '+'){
            st[t] = 1;
        }
        else{
            for(long long i=max(mp[t],1ll*t),cnt=0;cnt<N;cnt++,i+=t){
                if(!st[i]){
                    mp[t] = i;
                    printf("%lld\n",i);
                    break;
                }
            }
        }
    }
}
int main(){
    int _=1;
    while(_--) solve();
    return 0;
}

D2. Balance (Hard version)

在这里插入图片描述

Sample input1

18

  • 1
  • 2
    ? 1
  • 4
    ? 2
  • 6
    ? 3
  • 7
  • 8
    ? 1
    ? 2
  • 5
    ? 1
  • 1000000000000000000
    ? 1000000000000000000
  • 4
    ? 1
    ? 2

Sample output1

3
6
3
3
10
3
2000000000000000000
3
4

Sample input2

10

  • 100
    ? 100
  • 200
    ? 100
  • 100
    ? 100
  • 50
    ? 50
  • 50
    ? 50

Sample ouput2

200
300
100
100
50

题意:

跟上个题的区别就是一个remove,从集合中删除一个数。

思路:

要先弄清楚这个小小的remove它的影响是什么,会发现它只会对在remove之前询问过的因子和remove之后询问的因子产生结果不相同的影响,通俗一点来讲吧,假如说当前remove的数是x,他之前的某个因子 t 询问过,已经把这个x给遍历过去了,那么说这个x就是 t 的绊脚石,因为他上次维护的右端点是tk 把,这个tk是一定大于这个x的,相当于是出现了一个1111011111这类的情况,所以说咱们只需要将每个因子的绊脚石给找出来,下次询问的时候先查绊脚石有没有在集合内,然后再跟D1一样就行,下面请看代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
map<long long,set<long long> > mp;
map<long long,vector<long long> > e;
map<long long,long long> mpp,st;
void solve(){
    int m;
    scanf("%d",&m);
    while(m--){
        char s[2];
        long long t;
        scanf("%s%lld",s,&t);
        if(*s == '+'){
            st[t] = 1;
        }
        else if(*s == '-'){
            st[t] = 0;
            for(int i=0;i<e[t].size();i++) mp[e[t][i]].insert(t);
        }
        else{
        	bool flag = false;
        	set<long long> q;
            for(auto x : mp[t]){
                if(!st[x]){
                	printf("%lld\n",x);
                	flag = true;
                	break;
				}
				else q.insert(x);
            }
            for(auto x : q) mp[t].erase(x);
            if(flag) continue;
            for(long long i=max(mpp[t],1ll*t),cnt=0;cnt<N;cnt++,i+=t){
                if(!st[i]){
                    mpp[t] = i;
                    printf("%lld\n",i);
                    break;
                }
                e[i].push_back(t);
            }
        }
    }
}
int main(){
    int _=1;
    while(_--) solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

宇智波一打七~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值