CodeForces - 1370F2 The Hidden Pair (Hard Version)(交互題+二分)

題目鏈接:點擊查看

題目大意:給出一棵無向無根樹,事先確定好了兩個點 s 和 t ,現在需要通過詢問找到這兩個點

每次詢問可以給出一個點集,系統會返回點集中距離點 s 和點 t 距離之和最小的那個點以及其距離,如果有多個符合條件的點,會返回任意一個,比如詢問了點集 A = { s1 , s2 , ... , sk } ,則系統會返回一個點 v ∈ A 並且 dist( s , v ) + dist( t , v ) 最小

簡單版本的是可以詢問 14 次,困難版本的是隻能詢問 11 次

題目分析:14 和 11 次對應着數據範圍 1000 ,應該是 logn 的算法,所以我們應該儘量往上面靠攏

首先因爲無從下手,所以可以先問一遍全部的點,獲得到一個點 rt ,且距離之和爲 len,畫畫圖應該不難看出,點 rt 滿足的一個性質是,一定位於點 s 到點 v 的這條路徑上

下面的轉換可能比較難想,但是如果想到了的話剩下的就比較簡單了

我們可以以點 rt 點爲根,遍歷一遍整棵樹後跑出每個點的深度,此時對於每個點的深度以及 dist( s , v ) + dist( t , v ),可以看出這兩個值之間滿足着單調性,這樣我們就可以在深度上二分找到:距離之和等於 len 的最大深度的那個點,這個點就是 s 或者 t 中的一個點,再用找到的這個點建樹,詢問深度爲 len 的那一層的所有點,得到的答案就是另外一個點了

如果初始時設置 l = 1 , r = n ,那麼 F1 就這樣解決了,主要是該如何解決 F2?

很顯然,詢問全部的 n 個點,和知道其中一個點後再通過一次操作得到另外一個點,這兩次操作是無可避免的,優化點只能出自於二分上面 ,對於二分,我們可以做的優化就是縮小初始的範圍了

因爲初始時找到的 rt 一定是位於 s 到 t 的路徑上的,又因爲我們是要藉助二分尋找距離之和等於 len 的最大深度,那麼當這個 rt 在 s 到 t 這條路徑的最中間時,左端點最小,取到了 \tiny \left \lceil \frac{len}{2} \right \rceil 的位置,相應的,我們如果假設 rt 這個點初始時就是 s 點或者 t 點的話,那麼右端點最多也就是 len 了,所以右端點我們設置爲 min( len , max_deep ) 就好了,max_deep 是建樹後的最大深度

代碼:
 

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e3+100;

int n,max_deep;

vector<int>node[N],deep[N];

pair<int,int> query(int dep)
{
	printf("? %d",deep[dep].size());
	for(auto v:deep[dep])
		printf(" %d",v);
	puts("");
	fflush(stdout);
	int rt,len;
	scanf("%d%d",&rt,&len);
	return make_pair(rt,len);
}

void dfs(int u,int fa,int dep)
{
	max_deep=max(max_deep,dep);
	deep[dep].push_back(u);
	for(auto v:node[u])
	{
		if(v==fa)
			continue;
		dfs(v,u,dep+1);
	}
}

void build(int rt)
{
	max_deep=0;
	for(int i=0;i<=n;i++)
		deep[i].clear();
	dfs(rt,-1,0);
}

pair<int,int> get_root()
{
	printf("? %d",n);
	for(int i=1;i<=n;i++)
		printf(" %d",i);
	puts("");
	fflush(stdout);
	int rt,len;
	scanf("%d%d",&rt,&len);
	return make_pair(rt,len);
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("input.txt","r",stdin);
//  freopen("output.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int w;
	cin>>w;
	while(w--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			node[i].clear();
		for(int i=1;i<n;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			node[u].push_back(v);
			node[v].push_back(u);
		}
		pair<int,int>temp=get_root();
		int rt=temp.first,len=temp.second;
		build(rt);
		int l=(len+1)/2,r=min(max_deep,len),s;
		while(l<=r)
		{
			int mid=l+r>>1;
			temp=query(mid);
			int p=temp.first,d=temp.second;
			if(d==len)
			{
				s=p;
				l=mid+1;
			}
			else
				r=mid-1;
		}
		build(s);
		int t=query(len).first;
		printf("! %d %d\n",s,t);
		fflush(stdout);
		scanf("%*s");
	}













    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章