CF 试题泛做 *1600-2000

https://codeforces.com/problemset?tags=1600-2000

https://codeforces.com/contest/1632/problem/C

题意:对于或操作我们有

对于 T104 组数据,给两个数 a,b,1a<b106,b106 你可以执行任意次的以下的三个操作。

  1. a+=1
  2. b+=1
  3. a=a|b

问最少多少次的操作,可以使得 a==b

分析:

我们有 a|bmax(a,b)
因此第三个操作会使得 ab,如果等于就结束了,如果大于的话,接下来一直执行操作 2 才能使得 b 接近于 a。因此最多执行一次第三个操作。
如果不执行,那么花费 ba 的代价。
如果执行,那么执行之后 a!=b 只会因为某一位 a=1,b=0 的时候出现。我们可以选择在执行之前对 a 或者 b 进行 + 操作,或者在执行之后对 b 进行操作,从而满足要求。
如果是执行之后操作(目的是 (1,0)),那么和执行之前操作 b 的位数是一样的。并且在 (0,1) 的情况下,如果后操作会节省一些额外的 (1,0) 步骤。
因此只有两种可能:增加 a 然后取或,增加 b 然后取或。(取或的临界条件是取或之后就可以相等)
分别枚举计算即可。时间复杂度 O(b)
如果我们进行优化,直接算出增加的次数,那么时间复杂度可以优化到 O(logb):按位模拟,向前进位,累加贡献,直到没有这样的位数为止。

#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = a; i <= b; i++)
#define mod9 998244353
#define mod1 1000000007
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define endl '\n'
int t;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    cin >> t;
    while(t--) {
        int a, b; cin >> a >> b;
        int ans = b - a;
        for(int x = a; ; x++) {
            if(( x | b ) == b) {
                ans = min(ans, x - a + 1);
                break;
            }
        }
        for(int x = b; ; x++) {
            if(( x | a ) == x) {
                ans = min(ans, x - b + 1);
                break;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

https://codeforces.com/contest/1658/problem/D1

题意:给定 l=0,r,一开始有一个 lr 的排列,每个数异或 x 后变成了 a 数组。给定 a 数组,求一个可行的 x

分析:对于每一位,若 x 的这一位是 1,那么原排列每一个这一位都取反,否则不变。对于原数组(我们知道它是 [l,r])和 a,每一位分别计数看看有没有发生取反即可确定 x 的这一位是什么。

#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = a; i <= b; i++)
#define mod9 998244353
#define mod1 1000000007
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define endl '\n'
int a[200010];
bool xbit[20];  //每一位是什么
int x = 0;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    int t; cin >> t;
    while(t--) {
        int l, r; cin >> l >> r;
        f(i,l, r) cin>>a[i];
        int cnta = 0, cntn = 0;
        for(int i = 0; i <= 17; i++) {
            for(int j = 0; j <= r; j++) {
                if((a[j] >> i) & 1) cnta++;
                if((j >> i) & 1) cntn++;
            }
            
            if(cnta == cntn) xbit[i] = 0;
            else xbit[i] = 1;
            cnta = cntn = 0;
            x += (xbit[i] << i);
        }
        cout << x << endl;
        x = 0;
    }
    return 0;
}

l0 的时候,为什么这个做法假了呢?)
问题主要是在 cnta==cntn 的时候。
某一位上如果 0 的个数和 1 的个数相等,那么不管是异或 0 还是 1 都不会改变个数。但有的时候我们不总是可以任选一个。
例如以下例子:
01,100100,01
但为什么 l=0 的时候可以?因为如果这一位上是偶数,那么其前面的内容一定是成对出来的,如果整体异或 1 只是调换了位置,不会改变个数。
例如以下例子:
00,01,10,110101,00,11,10
(形式化地:若 ka 内,那么 k2i 也一定在。)

posted @   OIer某罗  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示