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\) 你可以执行任意次的以下的三个操作。
- \(a+=1\)
- \(b+=1\)
- \(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\) 也一定在。)