Ozon Tech Challenge 2020 (Div.1 + Div.2)D. Kuroni and the Celebration

D. Kuroni and the Celebration
time limit per test1 second
memory limit per test256 megabytes
inputstandard input
outputstandard output
This is an interactive problem.

After getting AC after 13 Time Limit Exceeded verdicts on a geometry problem, Kuroni went to an Italian restaurant to celebrate this holy achievement. Unfortunately, the excess sauce disoriented him, and he’s now lost!

The United States of America can be modeled as a tree (why though) with n vertices. The tree is rooted at vertex r, wherein lies Kuroni’s hotel.

Kuroni has a phone app designed to help him in such emergency cases. To use the app, he has to input two vertices u and v, and it’ll return a vertex w, which is the lowest common ancestor of those two vertices.

However, since the phone’s battery has been almost drained out from live-streaming Kuroni’s celebration party, he could only use the app at most ⌊n2⌋ times. After that, the phone would die and there will be nothing left to help our dear friend! 😦

As the night is cold and dark, Kuroni needs to get back, so that he can reunite with his comfy bed and pillow(s). Can you help him figure out his hotel’s location?

Interaction
The interaction starts with reading a single integer n (2≤n≤1000), the number of vertices of the tree.

Then you will read n−1 lines, the i-th of them has two integers xi and yi (1≤xi,yi≤n, xi≠yi), denoting there is an edge connecting vertices xi and yi. It is guaranteed that the edges will form a tree.

Then you can make queries of type “? u v” (1≤u,v≤n) to find the lowest common ancestor of vertex u and v.

After the query, read the result w as an integer.

In case your query is invalid or you asked more than ⌊n2⌋ queries, the program will print −1 and will finish interaction. You will receive a Wrong answer verdict. Make sure to exit immediately to avoid getting other verdicts.

When you find out the vertex r, print “! r” and quit after that. This query does not count towards the ⌊n2⌋ limit.

Note that the tree is fixed beforehand and will not change during the queries, i.e. the interactor is not adaptive.

After printing any query do not forget to print end of line and flush the output. Otherwise, you might get Idleness limit exceeded. To do this, use:

fflush(stdout) or cout.flush() in C++;
System.out.flush() in Java;
flush(output) in Pascal;
stdout.flush() in Python;
see the documentation for other languages.
Hacks

To hack, use the following format:

The first line should contain two integers n and r (2≤n≤1000, 1≤r≤n), denoting the number of vertices and the vertex with Kuroni’s hotel.

The i-th of the next n−1 lines should contain two integers xi and yi (1≤xi,yi≤n) — denoting there is an edge connecting vertex xi and yi.

The edges presented should form a tree.
Example
inputCopy
6
1 4
4 2
5 3
6 3
2 3
3
4
4
outputCopy
? 5 6
? 3 1
? 1 2
! 4
Note
Note that the example interaction contains extra empty lines so that it’s easier to read. The real interaction doesn’t contain any empty lines and you shouldn’t print any extra empty lines as well.

题意:
给出n个点,n-1条边,最多询问n/2次,每次询问uv,会给出uv的lca,求树的根。

思路:

  1. 首先把度为1的点都放进一个集合里面,然后每次询问从集合里面挑两个点出来,如果lca是其中一个,那么那个点就是树的根 。
  2. 为什么?因为度为1的点都是叶子节点,如果没有不同于uv两点的公共祖先,那么必定其中一点为根节点。
  3. 其余情况就是uv两点的公共祖先不是他们本身,因为他们的LCA在他们上面 所以他们不可能是根,所以把这两个结点从树上删去,更新相连的点的度,再把度为1的点放进集合。
  4. 重复以上操作,最后集合剩下的一个点就是树的根。

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define mod 1000000007
#define PI acos(-1)
#define fi first
#define se second
#define lowbit(x) (x&(-x))
#define mp make_pair
#define pb push_back
#define si size()
#define E exp(1.0)
#define fixed cout.setf(ios::fixed)
#define fixeds(x) setprecision(x)
using namespace std;
inline ll read(){ll s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;}void put1(){ puts("Yes") ;}void put2(){ puts("No") ;}
void debug(){printf("T   A   T\n");}void put3(){ puts("-1"); }ll qpf(ll a, ll b, ll p)
{ll ret = 0;while(b){if(b & 1) ret = (ret + a) % p;a = (a + a) % p;b >>= 1;}
return ret % p ;}ll qp(ll a, ll n, ll p){ll ret = 1;while(n){if(n & 1) ret = qpf(ret, a, p);
a = qpf(a, a, p);n >>= 1;}return ret % p ;}//��=acos(L/2R);

const int manx=2e5+5;

ll d[manx];
vector<ll>g[manx];
set<ll>s;
void del(ll u){
    for(auto v: g[u]){
        d[v]--;
        if(d[v]==1) s.insert(v);
    }
}
void out(ll x){
    cout<<"! "<<x<<endl; cout.flush();
}
int main()
{
 	ll n=read();
 	for(int i=1;i<n;i++){
 		ll u=read(),v=read();
 		g[u].pb(v);
		g[v].pb(u);
		d[u]++,d[v]++;
    }
    for(int i=1;i<=n;i++)
        if(d[i]==1)
            s.insert(i);
	while(s.si>1){
        ll u=*s.begin(),v=*(++s.begin());
        cout<<"? "<<u<<" "<<v<<endl,cout.flush();
		ll lca; cin>>lca;
		if(lca==u || lca==v){
            out(lca);
            return 0;
		}
		del(u),del(v);
		s.erase(u),s.erase(v);
	}
    out(*s.begin());
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值