二进制集合划分
一个比较经典的问题。在一颗树上求任意两点距离最大的值,暴力的方法是枚举每一个点和其他点做比较,找出最大的值(超时)。这里有两种方法
-
定一个点1,通过二分的方法找出从点1到距离最远的点,记为点p,然后在不考虑点p的情况下再用同样的方法求出点q,
那么点1到点p,和q的距离和就是树上任意两点的最大距离。 -
(尚未证明,当定理用即可) 假设有n个点,我们可以做 log(n)次集合划分,第一次划分分别为 n 的二进制数从右向左数第一位为 0 和 1的情况,
第二次划分分别为 n 的二进制数从右向左数第二位为 0 和 1的情况,以此类推一共划分 log(n)次,这种集合划分能够不遗漏的求出任意两点之间的最大距离。
#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
typedef long long ll;
const int Maxn = 3e5+10;
const int INF = 0x3f3f3f3f;
const int Mod = 1e9+7;
vector <int> a, b;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(false); cout.tie(false);
int T;
cin >> T;
while(T--) {
int n;
ll ans = 0;
cin >> n;
for(int i = 0; i < 10; ++i) {
a.clear();
b.clear();
for(int j = 1; j <= n; ++j) {
if((j>>i)&1) b.push_back(j);
else a.push_back(j);
}
if(a.size() == 0 || a.size() == n) break;
cout << a.size() << " " << b.size() << " ";
for(int k = 0; k < a.size(); ++k) cout << a[k] << " ";
for(int k = 0; k < b.size(); ++k) cout << b[k] << " ";
cout << endl;
ll tmp;
cin >> tmp;
ans = max(ans, tmp);
}
cout << "-1 " << ans << endl;
}
return 0;
}
浙公网安备 33010602011771号