• [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$的路径即其子树中,询问结果为$ e u,r_{2i-1},r_{2i}$,再询问一次$(u,v,r_{2i-1})$即可

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

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

    下面,来考虑操作次数:

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

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

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

    显然有$egin{cases}s_{i}le lfloorfrac{n}{2} floor&(i=1)\s_{i}le lfloorfrac{n-1}{i} floor&(2le ile d)end{cases}$,也即$T_{1}(n)=max(T(lfloorfrac{n}{2} floor)+1,max_{2le ile lceilfrac{D}{2} ceil}T(lfloorfrac{n-1}{2i-1} floor)+i)$

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

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

    最终$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 imes 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
  • 相关阅读:
    《UNIX环境高级编程》笔记--UNIX标准化及实现
    SPOJ1811最长公共子串问题(后缀自动机)
    一个leetcode解题报告类目,代码很简洁
    字符压缩题目
    求最佳会议地点
    实现树的横向指针
    lower_bound与upper_bound
    求到所有房子距离和最小的新房子
    增加限制条件的矩阵求和
    切分数组来得到一定的和
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14955245.html
Copyright © 2020-2023  润新知