[loj3031]聚会

对于一棵树(初始仅包含节点0),不断加入一个不在树中的节点$u$(不需要随机),并维护这棵树

具体的,对这棵树点分治,假设当前重心$v$有$d$个子树,假设其中第$i$个子树根为$r_{i}$,子树大小为$s_{i}$,且不妨假设子树大小单调不上升(即$s_{1}\ge s_{2}\ge ...\ge s_{d}$)

初始令$i=1$,并询问$(u,r_{2i-1},r_{2i})$,并分类讨论:

1.若$u$在$r_{2i-1}$或$r_{2i}$的子树中,询问结果为$r_{2i-1}$或$r_{2i}$,递归询问结果的子树即可

2.若$u$在$r_{2i-1}$或$r_{2i}$到$v$的路径即其子树中,询问结果为$\ne u,r_{2i-1},r_{2i}$,再询问一次$(u,v,r_{2i-1})$即可

3.若$u$不为上述两种情况,询问结果为$v$,若$2i\ge d$则$u$为$v$的新儿子,否则令$i$增加1并重复此过程

(特别的,若$d$为奇数,我们认为$r_{d+1}=v$,且若为第二种情况,则一定在$r_{d}$到$v$的路径上)

下面,来考虑操作次数:

令$T(n)$为$n$个节点的子树中最大询问次数,考虑$u$结束的情况,即以下三种——

(为了方便,记$D=\min(n-1,18)$,显然$d\le D$)

1.在第一种情况下结束:假设在$i$时结束,则至多需要$T(s_{2i-1})+i$次(由于$s_{2i-1}\ge s_{2i}$)

显然有$\begin{cases}s_{i}\le \lfloor\frac{n}{2}\rfloor&(i=1)\\s_{i}\le \lfloor\frac{n-1}{i}\rfloor&(2\le i\le d)\end{cases}$,也即$T_{1}(n)=\max(T(\lfloor\frac{n}{2}\rfloor)+1,\max_{2\le i\le \lceil\frac{D}{2}\rceil}T(\lfloor\frac{n-1}{2i-1}\rfloor)+i)$

2.在第二种情况下结束:此时即询问$\lceil\frac{d}{2}\rceil+1$,即$T_{2}(n)=\lceil\frac{D}{2}\rceil+1$

3.在第三种情况下结束,此时即询问$\lceil\frac{d}{2}\rceil$次,同理即$T_{3}(n)=\lceil\frac{D}{2}\rceil$

最终$T(n)=\max(T_{1}(n),T_{2}(n),T_{3}(n))$,初始状态为$T(1)=0$(此时将$u$作为该点的儿子即可),最大询问次数为$\sum_{i=1}^{n-1}T(i)$

经过计算,可得在$n=2\times 10^{3}$时,该值为39371(官方题解给出的值是39632),可以通过

 1 #include<bits/stdc++.h>
 2 #include "meetings.h"
 3 using namespace std;
 4 #define N 2005
 5 struct Edge{
 6     int nex,to;
 7 }edge[N<<1];
 8 vector<int>v;
 9 int E,rt,head[N],vis[N],sz[N],Vis[N];
10 bool cmp(int x,int y){
11     return sz[x]>sz[y];
12 }
13 void add(int x,int y){
14     edge[E].nex=head[x];
15     edge[E].to=y;
16     head[x]=E++;
17 }
18 void get_sz(int k,int fa){
19     sz[k]=1;
20     for(int i=head[k];i!=-1;i=edge[i].nex)
21         if ((!vis[edge[i].to])&&(edge[i].to!=fa)){
22             get_sz(edge[i].to,k);
23             sz[k]+=sz[edge[i].to];
24         }
25 }
26 void get_rt(int k,int fa,int s){
27     int mx=s-sz[k];
28     for(int i=head[k];i!=-1;i=edge[i].nex)
29         if ((!vis[edge[i].to])&&(edge[i].to!=fa)){
30             get_rt(edge[i].to,k,s);
31             mx=max(mx,sz[edge[i].to]);
32         }
33     if (mx<=s/2)rt=k;
34 }
35 void dfs(int k,int u){
36     get_sz(k,0);
37     get_rt(k,0,sz[k]);
38     get_sz(rt,0);
39     vis[rt]=1;
40     v.clear();
41     for(int i=head[rt];i!=-1;i=edge[i].nex)
42         if (!vis[edge[i].to])v.push_back(edge[i].to);
43     sort(v.begin(),v.end(),cmp);
44     if (v.size()&1)v.push_back(rt);
45     for(int i=0;i<v.size();i+=2){
46         int ans=Query(u,v[i],v[i+1]);
47         if (ans==rt)continue;
48         if ((ans==v[i])||(ans==v[i+1])){
49             dfs(ans,u);
50             return;
51         }
52         int p=v[i];
53         if (Query(u,rt,v[i])==rt)p=v[i+1];
54         add(ans,rt),add(ans,p);
55         for(int j=head[rt];j!=-1;j=edge[j].nex)
56             if (edge[j].to==p){
57                 edge[j].to=ans;
58                 break;
59             }
60         for(int j=head[p];j!=-1;j=edge[j].nex)
61             if (edge[j].to==rt){
62                 edge[j].to=ans;
63                 break;
64             }
65         if (ans!=u){
66             add(u,ans);
67             add(ans,u);
68             Vis[ans]=1;
69         }
70         return;
71     }
72     add(rt,u),add(u,rt);
73 }
74 void Solve(int n){
75     memset(head,-1,sizeof(head));
76     for(int i=1;i<n;i++)
77         if (!Vis[i]){
78             memset(vis,0,sizeof(vis));
79             dfs(0,i);
80         }
81     for(int i=0;i<n;i++)
82         for(int j=head[i];j!=-1;j=edge[j].nex)
83             if (i<edge[j].to)Bridge(i,edge[j].to);
84 }
View Code

 

posted @ 2021-06-30 16:43  PYWBKTDA  阅读(61)  评论(0编辑  收藏  举报