CF 试题泛做 *1600-2000

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

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

题意:对于或操作我们有

对于 \(T\le 10^4\) 组数据,给两个数 \(a,b,1\le a<b\le10^6, \sum b \le 10^6\) 你可以执行任意次的以下的三个操作。

  1. \(a+=1\)
  2. \(b+=1\)
  3. \(a=a|b\)

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

分析:

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

#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\),一开始有一个 \(l \sim r\) 的排列,每个数异或 \(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;
}

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

posted @ 2022-05-28 21:19  OIer某罗  阅读(13)  评论(0编辑  收藏  举报