传送门
一道很有趣的交互题
测试点
,直接
次修改
次查询就可以了
测试点
,这一部分是两两配对的点,可以整体二分改一半然后查令一半,如果有显然是与前一半配对
还有一个骚气解法是让每个点有
的概率被修改,显然同色的是连了边的,递归处理
测试点
,这是一颗上面结点比下面小的树,问题转换为确定每一个点的父亲,由于父亲一定比它小,所以二分一个点然后修改一个前缀,整体二分就可以了
测试点
,这是一条链
有一个骚气的解法:
对每一个二进制为考虑,修改这一位为 1 的点,一个点是 0 是 1 就是它旁边的一个或两个点的异或和
对 0 确定一下它的位置然后向两边扩展即可
考虑随机化,我们跟
一样,先随机修改
个点,把修改的集合记为
然后对所有点查询,如果为 1 那么一定与
中的有连边
然后跟上面的整体二分一样,我们对
集合修改一半,如果之前为 1 的有变动,那么肯定跟这一半有连边
期望的复杂度是
谁也想不到一道
的压轴题会使用到随机化一类的算法
不过当没有思路的时候的确应该往这方面想
另外,整体二分和二进制的思路也十分巧妙
#include "explore.h"
#include<bits/stdc++.h>
#define cs const
using namespace std;
int Lm, Lq, Lc, n, m;
namespace Small{
cs int N = 505;
bool lit[N];
void explore(int n, int m){
for(int i = 0; i < n-1; i++){
modify(i);
for(int j = i+1; j < n; j++){
bool x = query(j);
if(x ^ lit[j]){ report(i, j); lit[j] = x;}
}
}
}
}
namespace A{
cs int N = 2e5 + 5;
int a[N], tmp[N];
bool cmp(int a, int b){ return tmp[a] < tmp[b]; }
void work(int l, int r){
if(l + 1 == r){ report(a[l], a[r]); return; }
if(l >= r) return;
random_shuffle(a + l, a + r + 1);
for(int i = l; i <= r; i++)
if(rand() & 1) modify(a[i]);
for(int i = l; i <= r; i++) tmp[a[i]] = query(a[i]);
sort(a + l, a + r + 1, cmp);
int mid = l - 1;
for(int i = l; i <= r; i++){
if(tmp[a[i]] == 0) mid = i;
} work(l, mid); work(mid+1, r);
}
void explore(int n, int m){
for(int i = 0; i < n; i++) a[i] = i;
work(0, n-1);
}
}
namespace Tree{
cs int N = 2e5 + 5;
int a[N], tmp[N], b[N];
void work(int l, int r, int L, int R){
if(l == r){ for(int i = L; i <= R; i++) report(a[i], l); return; }
int mid = (l+r) >> 1;
for(int i = l; i <= mid; i++) modify(i);
int ct = 0;
for(int i = L; i <= R; i++){
if(a[i] <= mid || query(a[i])) tmp[a[i]] = 0, ++ct;
else tmp[a[i]] = 1;
} int ql = L, qr = L + ct;
for(int i = L; i <= R; i++){
if(tmp[a[i]] == 0) b[ql++] = a[i];
else b[qr++] = a[i];
}
for(int i = l; i <= mid; i++) modify(i);
work(l, mid, L, R);
work(mid+1, r, L, R);
}
void explore(int n, int m){
for(int i = 0; i < n; i++) a[i] = i, cout << a[i] << endl;
work(0, n-1, 1, n-1);
}
}
namespace C{
cs int N = 2e5 + 5;
int vl[N], tp[N];
void explore(int n, int m){
int up = 1;
for(;(1<<up)<n; ++up);
for(int c = 0; c < up; c++){
for(int i = 0; i < n; i++){ if(i>>c&1) modify(i), tp[i] ^= 1; }
for(int i = 0; i < n; i++) tp[i] ^= query(i);
for(int i = 0; i < n; i++) vl[i] |= tp[i]<<c;
for(int i = 0; i < n; i++){ if(i>>c&1) modify(i); tp[i] = 0; }
}
modify(0);
int x = 0, y = 0;
for(int i = 1; i < n; i++){ int t = query(i); if(t){ if(x) y = i; else x = i; } }
report(0, x);
if(y) report(0, y);
while(true){ int nxt = vl[x]; if(!nxt) break; report(x, nxt); vl[nxt] ^= x; x = nxt; }
if(y) while(true){ int nxt = vl[y]; if(!nxt) break; report(y, nxt); vl[nxt] ^= y; y = nxt; }
}
}
namespace Hard{
cs int N = 3e5 + 5;
int a[N], b[N], cm, S[N];
typedef pair<int, int> pi;
vector<pi> tmp;
vector<int> e[N];
map<pi,int> mp;
bool die[N];
bool va[N], vb[N];
void adde(int x, int y){
if(x > y) swap(x, y); if(mp.count(pi(x, y))) return;
mp[pi(x, y)] = 1; ++cm; report(x, y); tmp.push_back(pi(x, y));
die[x] = check(x); die[y] = check(y);
}
void mod(int x){
S[x] ^= 1;
for(int i = 0; i < e[x].size(); i++) S[e[x][i]] ^= 1;
modify(x);
}
void work(int la, int ra, int lb, int rb){
if(lb > rb) return;
if(la == ra){ for(int i = lb; i <= rb; i++) adde(b[i], a[la]); return; }
random_shuffle(a + la, a + ra + 1);
random_shuffle(b + lb, b + rb + 1);
int cta = 0, ctb = 0;
for(int i = la; i <= ra; i++){
if(rand() & 1) mod(a[i]), va[a[i]] = true, ++cta;
else va[a[i]] = false;
}
for(int i = lb; i <= rb; i++){
if(query(b[i]) ^ S[b[i]]) vb[b[i]] = true, ++ctb;
else vb[b[i]] = false;
}
static int tpa[N], tpb[N];
int qla = la, qra = la + cta, qlb = lb, qrb = lb + ctb;
for(int i = la; i <= ra; i++){ if(va[a[i]]) mod(a[i]), tpa[qla++] = a[i]; else tpa[qra++] = a[i]; }
for(int i = lb; i <= rb; i++){ if(vb[b[i]]) tpb[qlb++] = b[i]; else tpb[qrb++] = b[i]; }
for(int i = la; i <= ra; i++) a[i] = tpa[i];
for(int i = lb; i <= rb; i++) b[i] = tpb[i];
work(la, qla-1, lb, qlb-1);
work(qla, ra, qlb, rb);
}
void explore(int n, int m){
while(cm < m){
for(int i = 0; i < tmp.size(); i++){
int u = tmp[i].first, v = tmp[i].second;
e[u].push_back(v); e[v].push_back(u);
} tmp.clear();
int la = 0, lb = 0;
for(int i = 0; i < n; i++){
if(die[i]) continue;
if(rand() % 2) a[++la] = i, mod(i);
}
for(int i = 0; i < n; i++){
if(die[i]) continue;
if(query(i) != S[i]) b[++lb] = i;
}
for(int i = 1; i <= la; i++) mod(a[i]);
work(1, la, 1, lb);
}
}
}
void explore(int n, int m){
srand(time(0));
if(n == 3 || n <= 500){ Small::explore(n, m); return; }
if(n % 10 == 8){ A::explore(n, m); return; }
if(n % 10 == 7){ Tree::explore(n, m); return; }
if(n % 10 == 6){ C::explore(n, m); return; }
Hard::explore(n, m);
}