Codeforces Round #781 (Div. 2)
vp 状态不错,还剩将近一个小时就 AK 了,中间还摆了挺久。
CF1665A GCD vs LCM
直接让 \(\gcd(a,b) = \lcm(c , d) = 1\) 就好了,即 \((x-3 , 1 , 1 ,1)\)。
CF1665B Array Cloning Technique
肯定选最多的数全部挪进一个数组里面,于是算出交换次数。每次该种数个数会增加一倍,倍增看看增长多少轮就好。
CF1665C Tree Infection
信息复杂,考虑二分答案,费用提前计算。
显然每个点只能进行一次费用提前计算。
每个时间点 \(i\) 选择没有儿子中毒且剩余儿子最多的点将他的儿子毒一个然再算上终局凭借这一个儿子可以毒多少个儿子,如果全部点儿子中毒那就随便选一个毒。
于是就可以判断是否可行。
CF1665D GCD Guess
有意思的构造,可惜询问次数太作了。
一眼看过去发现我们可以通过一次操作判断 \(x\) 的奇偶性,结合询问量,我们很快想到依次判断每一位是 1
还是 0
,简单讨论一下就可以做了。
但题目很阴间,他卡了一下边界,就是我们询问的数不能超过 \(2 \times 10^9\)。
因此我们要假设当前位为 1
才能满足限制。
int main() {
int T;
read(T);
while(T -- > 0) {
int cur = 0;
for (int i = 0; i <= 29; ++i) {
cur ^= (1 << i);
LL a = (1 << (i + 1)) - cur , b = a + (1 << (i + 1));
cout << "? " << a << " " << b << endl;
int res = 0;
cin >> res;
if(res % (1 << (i + 1))) cur ^= (1 << i);
}
cout << "! " << cur << endl;
}
return 0;
}
CF1665E MinimizOR
一眼看过去就是需要从高到低讨论位是 1
还是 0
。
由于是询问区间,于是考虑建可持久化 trie
树。
从高到低走,如果走 0
边不能凑出两个满足前面所有限制的点,那就走 1
边,但是因为我们没有对这一为限定,因此 0
边的点不能忽略,但注意到此时 0
边下面最多只有一个点,于是记录下这个点,下面计算点数时暴力算一下。
如果可以,那就走 0
边,但是需要删除前面记录的点中该位不是 0
的,因为此时我们是限定该位必须为 0
。
于是时间复杂度 \(O(n\log n + q \log^2 n)\)。
const int MAXN = 1e5 + 5;
const int N = (1 << 30) - 1;
const int D = (1 << 29);
int tr[MAXN * 32] , ls[MAXN * 32] , rs[MAXN * 32] , val[MAXN * 32];
int n , num , rt[MAXN];
int update(int l , int r , int now , int d , int x) {
int dir = ++num;
val[dir] = x;
if(l == r) {
tr[dir] = tr[now] + 1;
return dir;
}
tr[dir] = tr[now] + 1;ls[dir] = ls[now] , rs[dir] = rs[now];
int mid = (l + r) >> 1;
if(!(x & d)) ls[dir] = update(l , mid , ls[now] , d >> 1 , x);
else rs[dir] = update(mid + 1 , r , rs[now] , d >> 1 , x);
return dir;
}
vector <int> cur;
int ans;
int calc(int d) {
int res = 0;
for (auto v:cur) res += !(v & d);
return res;
}
void find(int l , int r , int v , int u , int d) {
if(!d) return;
int mid = (l + r) >> 1;
if(tr[ls[u]] - tr[ls[v]] + calc(d) <= 1) {
if(tr[ls[u]] - tr[ls[v]]) cur.push_back(val[ls[u]]);
ans ^= d;
find(mid + 1 , r , rs[v] , rs[u] , d >> 1);
}
else {
for (int i = 0; i < (int)cur.size(); ++i) if(cur[i] & d) cur[i] = N;
find(l , mid , ls[v] , ls[u] , d >> 1);
}
}
int main() {
int T;
read(T);
while(T -- > 0) {
read(n);
for (int i = 1; i <= n; ++i) {
int x;read(x);
rt[i] = update(0 , N , rt[i - 1] , D , x);
}
int Q;read(Q);
while(Q -- > 0) {
int l , r;
read(l),read(r);
cur.clear();ans = 0;
find(0 , N , rt[l - 1] , rt[r] , D);
write(ans);
}
for (int i = 1; i <= num; ++i) val[i] = tr[i] = ls[i] = rs[i] = 0;
for (int i = 1; i <= n; ++i) rt[i] = 0;
num = 0;
}
return 0;
}