Codeforces Round #746 (Div. 2)D. Hemose in ICPC ? (交互 二分+dfs序)

题目链接

给定带边权的无根树,定义dis(u,v)等于u到v简单路径上所有边的GCD,每次可以询问一个点集,会返回点集中所有点对的dis最大值,最后输出这棵树中,使得dis(u,v)最大的u,v

(2<=n<=1e3 ,最多询问12次)

  • 首先GCD具有只减不增的性质,最后答案就等价于输出边权最大的边的两个顶点。每次询问也是返回的最大边权。
  • 看到询问次数,12,点只有1000个,说明主体算法肯定是二分了。
  • 我们首先可以询问所有的点,返回的一定是最大边权W。
  • 我们想到,能不能把所有的边分成两部分,左边右边各询问一次,看看最大边权W在哪边,这两部分相当于把树分成了两个连通块,所有的边仍然是有序的。
  • 我们可以用边的dfs序,在这个序列上,边始终是按访问顺序排列,可以进行二分。每次二分相当于把1个连通块分成了两个。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 1e3 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//给定带点权的树 
int a[maxn];
int clk,n;
vector<int> g[maxn];
pii edg[maxn];//对边求dfs序 然后二分
void dfs(int rt,int fa) 
{
    
    
	for(int i:g[rt]) 
	{
    
    
		if(i==fa) continue;
		edg[++clk]={
    
    rt,i};//边的dfs序
		dfs(i,rt);
	}
}
inline int ask(int l,int r)//[l,r]这些边当中的最大边权
{
    
    
	set<int> p;
	for(int i=l;i<=r;i++) 
	{
    
    
		p.insert(edg[i].first);
		p.insert(edg[i].second);
	}
	printf("? %d",p.size());
	for(int i:p) printf(" %d",i);
	puts("");
	fflush(stdout);
	int ret;
	scanf("%d",&ret);
	return ret;
}
int main()
{
    
    
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++)
	{
    
    
		scanf("%d %d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(1,0);
	int l=1,r=n-1;
	int target=ask(l,r);//最大边权
	int ans;
	while(l <= r) 
	{
    
    
		int mid=l+r>>1;
		if(ask(l,mid) == target) 
		{
    
    
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	printf("! %d %d\n",edg[ans].first,edg[ans].second);
	fflush(stdout);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/120994584
今日推荐