Codeforces1990 E1/E2. Catch the Mole(Easy Version/Hard Version) (交互题,思维,均值不等式复杂度计算)

题意:

在这里插入图片描述
n<=5000

E1解法:
三个重要结论:
1. 如果松鼠不在子树x中,那么子树x可以删除
2. 如果我们反复查询某个松鼠不在的叶子节点,松鼠会一直往根节点走
3. 如果松鼠在子树x中,我们反复查询松鼠不在的叶子节点,可以把松鼠从子树赶出去.

做法:
预处理dep[i]和mdep[i], 其中mdep[i]表示点i子树中的最大深度.

我们可以这样找到松鼠:
1. 找到某个点nd,该点满足mdep[i]-dep[i]+1 == t
满足这个条件意味着子树nd的子链最大长度为t
2. 如果能够找到满足条件的点nd,我们判断松鼠是否在子树nd中:
a. 如果不在子树nd中,则将子树nd删除。
由于最大子链长度为t,则子树nd至少有t个节点,
因此这样的操作次数一定不超过n/t次.
b. 如果松鼠在子树nd中,由于子树深度为t,我们通过最多t次操作将松鼠赶出子树nd.
具体做法是任选一个叶子反复查询,每次查询完查询松鼠是否还在子树nd中。
如果某一时刻松鼠不在子树nd中了,则松鼠一定在pre[pre[nd]]位置。
3. 如果没有满足条件的点nd,那么说明当前树的最大深度不超过t,因此我们进行t次驱赶操作就能将松鼠赶到节点1.

复杂度分析:
对于操作2.a,我们最多操作n/t次
对于操作2.b,我们最多操作2t次
对于操作3,该操作只在2.b未生效时出现,此时我们最多操作t次.

因此最坏情况下,我们的操作次数大概为n/t+2t次.
由均值不等式可知当n/t==2t时,n/t+2t达到最小值,此时t=sqrt(n/2).
由于N最大值为5000, 因此t=sqrt(5000/2)=sqrt(2500)=50.
操作次数最大值为2*sqrt(2t*n/t)=2*sqrt(2N)=200.
满足题目操作次数<=300的条件.
E2 解法:
复杂度瓶颈在于操作2.b,每次查询叶子之后都要查询hd,判断松鼠是否走出了子树hd。
其实直接进行t次驱赶操作后,松鼠一定在[1,hd]这条链上,我们对链进行二分即可.
复杂度就从2t优化为了t+log(n)

总操作次数为n/t+t+log(n)
当n/t==t时,即t=sqrt(N)时最小,总操作次数为2*sqrt(N)+log(N), 
大概=100*sqrt(2)+log(N).
100*sqrt(2)大约144
log(N)<=13
总操作次数<=160, 能够满足题目要求.
E1 Code:
#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define int long long
#define PI pair<int, int>
const int maxm=2e6+5;
// const int mod=998244353;
const int mod=1e9+7;

int n;
vector<int>g[maxm];
int del[maxm];
int dep[maxm];
// mdep[i]表示子树i中的最大深度
int mdep[maxm];
int pre[maxm];
int ask(int x){
   
   
  cout<<"? "<<x<<endl;
  int ans;cin>>ans;return ans;
}
void out(int x){
   
   
  cout<<"! "<<x<<endl;
}
// 初始化pre[]
void init(int x,int fa){
   
   
  pre[x]=fa;
  for(int v:g[x]){
   
   
    if(v==fa)continue;
    init(v,x);
  }
}
// 计算dep[]和mdep[]
void dfs(int x) {
   
   
  mdep[x]=dep[x];
  for(int v:g[x]){
   
   
    if(v==pre[x]||del[v
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值