「NOI2019」I 君的探险 (交互题)(随机化)(整体二分)(二进制)

传送门
一道很有趣的交互题
测试点 1 − 5 1-5 15,直接 n n n 次修改 n 2 n^2 n2 次查询就可以了
测试点 6 − 9 6-9 69,这一部分是两两配对的点,可以整体二分改一半然后查令一半,如果有显然是与前一半配对
还有一个骚气解法是让每个点有 1 / 2 1/2 1/2 的概率被修改,显然同色的是连了边的,递归处理
测试点 10 − 11 10-11 1011,这是一颗上面结点比下面小的树,问题转换为确定每一个点的父亲,由于父亲一定比它小,所以二分一个点然后修改一个前缀,整体二分就可以了
测试点 12 − 14 12-14 1214,这是一条链
有一个骚气的解法:
对每一个二进制为考虑,修改这一位为 1 的点,一个点是 0 是 1 就是它旁边的一个或两个点的异或和
对 0 确定一下它的位置然后向两边扩展即可

考虑随机化,我们跟 6 − 9 6-9 69 一样,先随机修改 n / 2 n/2 n/2 个点,把修改的集合记为 S S S
然后对所有点查询,如果为 1 那么一定与 S S S 中的有连边
然后跟上面的整体二分一样,我们对 S S S 集合修改一半,如果之前为 1 的有变动,那么肯定跟这一半有连边
期望的复杂度是 O ( m l o g ( m ) ) O(mlog(m)) O(mlog(m))


谁也想不到一道 N O I NOI NOI 的压轴题会使用到随机化一类的算法
不过当没有思路的时候的确应该往这方面想
另外,整体二分和二进制的思路也十分巧妙

#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); 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值