Codeforces Global Round 18(A-C)

Codeforces Global Round 18(A-C)

第一次写,表达不清晰的请见谅。

A.Closing The Gap

题意:

给一个序列a,每次操作可以让序列a中一个数加1和另一个数减1

在多次操作后,问 m a x ( a ) − m i n ( a ) max(a) - min(a) max(a)min(a)的最小值。


思路:

不难发现,我们的总和是不变的。在没有操作次数的限制下,我们一定可以把所有数弄相等或者差1。所以我们只需要算出总和看能不能平分,可以的话答案就是0,否则就是1。


代码

#include<bits/stdc++.h>
#include<set>
#include<deque>
#include<map>
 
using namespace std;
typedef long long ll;
typedef pair<ll,ll>PII;
const int N = 1e6+10,inf = 0x3f3f3f3f;
int a[N];
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
 
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        ll sum = 0;
        for(int i = 1; i <= n; i++){
            cin >> a[i];
            sum += a[i];
        }
        if(sum % n == 0){
            cout << 0 << endl;
        }
        else{
           cout <<1<<endl;
        }
    }
    return 0;
}
/*
input
3 			
3       	
10 10 10	
4
3 2 1 2
5
1 2 3 1 5	
output
0
0
1
*/

B.And it’s Non-Zero

题意:

给一个l(左边界)r(右边界),我们的目的是找到 最少从l到r的所有数中取去多少数后,

可以让l到r这个区间的按位与运算结果非0。


思路:

因为按位与是有0得0

要使答案非0,我们就要保证最后留下的每个数其二进制中在某个相同的位置全是1。

我们只需要在二进制中寻找每个数中每一位出现1的次数,找到出现次数的最大值。

这就说明这是我们需要留下的数。我们再用区间长度去减去它便可得到答案——要取出的数。

我们可以发现数据范围是 2 e 5 2e5 2e5 也就是说我们的数转换为二进制最多就20位

我们需要计算出一个区间中每个数每一位1出现的次数,在测试组数为 1 e 4 1e4 1e4的情况下,我们每次都去跑完区间是肯定会 T l e Tle Tle(我就tle了) ,所以我们要提前初始化数据。

#include<bits/stdc++.h>
#include<set>
#include<deque>
#include<map>

using namespace std;
typedef long long ll;
typedef pair<ll,ll>PII;
const int N = 2e5+10,inf = 0x3f3f3f3f;
int a[N][20]; // a[i][j] = 1 表示1——i之间所有数在其二进制下第j位上有1个1

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    //计算从1到2e5中每个数中二进制每一位1的个数
    for(int i = 1; i <= N; i++){
        for(int j = 0 ; j < 20 ; j ++){
            if(i & (1 << j) ) a[i][j]++;
            a[i][j] += a[i-1][j]; // 前缀和维护
        }
    }

    int t;
    cin >> t;
    while(t--){
        int l , r;
        cin >> l  >> r;
        int ans = 0;
        //找到这个区间中二进制1出现最多的次数
        for(int i = 0; i <20; i++){
            ans = max(ans,a[r][i] - a[l-1][i]);
        }
        ans = (r - l + 1) - ans;
        cout << ans <<endl;
    }
    return 0;
}
/*
input
5
1 2
2 8
4 5
1 5
100000 200000
output
1
3
0
2
31072
*/

C.Menorah

题意:

给两个由1和0组成的字符串 a,b。我们每次可以对a串的1进行操作:

维持当前位置1不变,其他位置的1变成0,0变成1;

问我们能否通过该操作把a串变成b串,如果可以 就输出最小操作次数,否则输出-1

思路:

通过观察,我们可以发现:

/*
1010 我们操作为1 的位置1
 |
1101 我们再操作原先为 0 的位置2
 |
0110 (结果)
*/

相当于我们可以通过两次操作交换原串中1和0的位置,并让其他位置保持不变。

于是:交换一次位置我们就要操作两次


我们要把从a串状态变成b串:

计算不相等的位置中 a串1与0的数量是否相等,相等的话我们可以用交换操作达成目的此时答案是

交换次数 * 2


特殊情况:

如果a串与b串中有同一位置等于1

我们就可以优先操作该位置,操作后该位置不变,之前与b串不相等的地方会相等,相等的地方会不等

/*
a:101   0101
b:101   1010
操作位置1
a:1  10   1010
b:1  01   1010
*/

所以我们也可以计算原a串中相同位置的0与1的数量是否相等,相同1的数量要减1(减去用来翻转的第一次),如果相同,我们同样可以通过交换操作达成目标,此时答案是交换次数 * 2 + 我们第一次的1次翻转


两个情况取最小值既是答案

#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
using namespace std;
const int N = 1e5 + 5;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> pe;
int a[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int t;
    cin >> t;
    while(t -- ){
        int n;
        cin >> n;
        string a , b;
        cin >> a >> b;
        int len = a.length();
        int ans = INF;
        int an = 0, bn = 0, cn = 0, dn = 0;
        for(int i = 0; i < len; ++ i){
            if(a[i] != b[i]){
                if(a[i] == '1') an++;
                else bn ++;
            }
            if(a[i] == b[i]){
                if(a[i] == '1') cn++;
                else dn ++;
            }
        }
        if(an == bn) ans = min(ans , an * 2);
        if(cn){
            cn --;
            if(cn == dn) ans = min(ans , cn * 2 + 1);
        }
        if(ans == INF) cout << - 1 << endl;
        else cout << ans << endl;
    }

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值