【CF1370F2】The Hidden Pair (Hard Version)
阿新 • • 發佈:2020-12-30
題目
題目連結:https://codeforces.com/problemset/problem/1370/F2
這是一道互動題。困難版與簡單版唯一的區別是互動次數限制。
本題有多組資料。
已知一棵 \(n\) 個頂點的樹(邊集已知),其中有兩個不同的頂點被暗中做了標記。你現在需要通過若干次詢問,猜出兩個被標記頂點的編號。
一次詢問的格式為 ? c x_1 x_2 ... x_c
,即代表你向互動庫請求關於 \(x_1,x_2,\cdots,x_c\) 這 \(c\) 個點的資訊。
對於一次詢問,互動庫的返回格式為 x d
,表示在詢問的集合中,到兩個被標記點的距離之和最小的點是 \(x\),這個最小值為 \(d\)
如果已經知曉答案,請用 ! x y
的格式來輸出你的答案,任意順序均可。在這之後,你會收到一個字串 Correct
或者 Incorrect
,代表你的猜測是否正確。如果收到了 Incorrect
,請立即終止程式,否則請繼續處理下一組資料。
對於每組資料,請你在不超過 \(11\) 次詢問之內給出答案。(Easy Version 是 \(14\) 次)
\(1\le t\le 10, 2\le n\le 1000\)。
思路
首先詢問一遍所的點,可以得到兩個標記點的距離 \(dis\),以及位於它們路徑上的一個點。
然後以得到的這個點開始 dfs,用 vector 記錄每一個點的深度。
然後二分深度,每次詢問這個深度上的所有點。如果得到的距離是 \(dis\)
可以在 \(O(\log n)\leq 10\) 次詢問找到其中一個標記點。以這個標記點為根 dfs,然後詢問所有深度為 \(dis\) 的點,得到的就是另一個標記點了。
操作次數為 \(1+10+1=12\),可以過 Easy Version。
我們發現,因為第一次二分的是較深的點,它的深度一定不小於 \(\lceil\frac{dis}{2}\rceil\),這樣我們二分的區間就變成了 \([\lceil\frac{dis}{2}\rceil,\min(dis,maxdep)]\)。顯然這個的上界是 \(\frac{n}{2}\leq 500\)
時間複雜度 \(O(Qn\log n)\)。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int n,Q,rt,dis,res,maxd,tot,head[N];
char ch[20];
vector<int> node[N];
struct edge
{
int next,to;
}e[N*2];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
void prework()
{
for (int i=0;i<=maxd;i++) node[i].clear();
memset(head,-1,sizeof(head));
tot=maxd=0;
}
void dfs(int x,int fa,int dep)
{
maxd=max(maxd,dep);
node[dep].push_back(x);
for (int i=head[x];~i;i=e[i].next)
if (e[i].to!=fa) dfs(e[i].to,x,dep+1);
}
int main()
{
scanf("%d",&Q);
while (Q--)
{
prework();
scanf("%d",&n);
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
// -----------------------------------------------------------
printf("? %d",n);
for (int i=1;i<=n;i++)
printf(" %d",i);
printf("\n");
fflush(stdout);
// -----------------------------------------------------------
scanf("%d%d",&rt,&dis);
dfs(rt,0,0);
int l=dis/2,r=min(maxd,dis),mid,x,y;
while (l<=r)
{
mid=(l+r)>>1;
// -----------------------------------------------------------
printf("? %d",node[mid].size());
for (int i=0;i<node[mid].size();i++)
printf(" %d",node[mid][i]);
printf("\n");
fflush(stdout);
// -----------------------------------------------------------
scanf("%d%d",&x,&y);
if (y==dis) l=mid+1,res=x;
else r=mid-1;
}
for (int i=0;i<=maxd;i++) node[i].clear();
dfs(res,0,0);
// -----------------------------------------------------------
printf("? %d",node[dis].size());
for (int i=0;i<node[dis].size();i++)
printf(" %d",node[dis][i]);
printf("\n");
fflush(stdout);
// -----------------------------------------------------------
scanf("%d%d",&x,&y);
printf("! %d %d\n",x,res);
fflush(stdout);
scanf("%s",ch);
}
return 0;
}