[交互 点分治] Codeforces 772E VK Cup 2017 - Round 2 E. Verifying Kingdom

本来的想法是能够找出叶子的兄弟 那么就把他们以及他们的父亲缩起来
但是很难找兄弟
看了题解发现是从初始一个点加点 相当于把缩的拆回去
通过点分治 我们找出当前树的重心 这里的重心是以叶子数量为权的
然后ask一下(g的左子树中的叶子,g的右子树中的叶子,当前要加的点)
有一些细节要处理
那么就可以确定当前点是在左子树 右子树还是子树外
询问复杂度 O(nlogn) 时间复杂度 O(n2)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cassert>
using namespace std;

inline int ask(int a1,int a2,int a3){ // 1: a1,a2 2: a2,a3 3: a1,a3;
  printf("%d %d %d\n",a1,a2,a3);
  fflush(stdout); char c;
  do c=getchar(); while (!(c>='A' && c<='Z'));
  return c=='X'?1:(c=='Y'?2:3);
}

const int N=2005;

int n;
int ncnt,pnt;
int ls[N],rs[N],ps[N],lc[N],rc[N];
int size[N],depth[N],sum,minv,rt;
int del[N];

inline void dfs(int u,int fa){
  size[u]=u<=n; depth[u]=depth[fa]+1; int maxv=0;
  int v;
  v=ls[u];
  if (v && v!=fa && !del[v]){
    dfs(v,u); size[u]+=size[v];
    maxv=max(maxv,size[v]);
  }
  v=rs[u];
  if (v && v!=fa && !del[v]){
    dfs(v,u); size[u]+=size[v];
    maxv=max(maxv,size[v]);
  }
  v=ps[u];
  if (v && v!=fa && !del[v]){
    dfs(v,u); size[u]+=size[v];
    maxv=max(maxv,size[v]);
  }
  maxv=max(maxv,sum-size[u]);
  if (u>n && maxv<minv) minv=maxv,rt=u;
}

inline void _new(int u){
  int a=++ncnt,b=++pnt; 
  if (ps[u]) { if (ls[ps[u]]==u) ls[ps[u]]=a; else rs[ps[u]]=a; }
  ps[a]=ps[u]; rs[a]=u; ls[a]=b;
  ps[u]=a; ps[b]=a;
  lc[b]=rc[b]=b; lc[a]=lc[ls[a]]; rc[a]=rc[rs[a]];
}

int S=0,Si;
inline void Size(int u,int fa){
  S++; Si+=u<=n; int v;
  v=ls[u]; if (v && v!=fa && !del[v]) Size(v,u);
  v=rs[u]; if (v && v!=fa && !del[v]) Size(v,u);
  v=ps[u]; if (v && v!=fa && !del[v]) Size(v,u);
}

int vst[N];

inline void Divide(int u){
  S=0; Si=0; Size(u,0);
  //assert(u); vst[u]=1;
  if (S==1) { _new(u); return; }
  sum=Si; minv=1<<30;
  dfs(u,0); 
  int g=rt; while ((!ls[g] || del[ls[g]]) && (!rs[g] || del[rs[g]])) g=ps[g];
  int t=ask(lc[ls[g]],rc[rs[g]],pnt+1);
  if (t==1){ //ls[u],rs[u]
    del[ls[g]]=del[rs[g]]=1;
    Divide(g);
  }else if (t==2){ //rs[u],g
    del[g]=1;
    Divide(rs[g]);
  }else{
    del[g]=1;
    Divide(ls[g]);
  }
}

int main(){
  //freopen("t.in","r",stdin);
  //freopen("t.out","w",stdout);
  scanf("%d",&n);
  ncnt=n; pnt=1; lc[1]=rc[1]=1;
  for (int i=2;i<=n;i++){
    for (int j=1;j<=2*n-1;j++) del[j]=vst[j]=0;
    Divide(1);
  }
  printf("-1\n");
  for (int i=1;i<=ncnt;i++)
    printf("%d ",ps[i]?ps[i]:-1);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值