• [loj3347]有趣的旅途


    考虑求出重心,以0为根建树,求出第 $i$个点的子树大小$sz[i]$($a(0,i)$),则满足$n-sz[i]le lfloorfrac{n}{2} floor$的$sz[i]$中的最小值必然合法

    证明:反证法,若其不合法,则其必然有一棵子树$sz[k]>sz[son]>lfloor frac{n}{2} floor$,那么$sz[son]$也满足该条件且更小,矛盾

    设重心为$r$,再求出每一个点的深度$d[i]$($h(r,i)$),以及在$r$的哪一个儿子的子树内(若$r$有三个儿子,询问其中两个即可),至此共计最多为$4(n-1)$次询问,在$Q$的范围之内

    考虑构造,构造的基本思路有3点:1.保证任意时刻$r$都为重心;2.任意相邻两点在$r$不同子树中;3.保证位置同奇偶(如第2和4个)的点深度不上升(很明显这样可以保证充分性)

    (根据思路1,可以发现最终必然只会剩下$r$,再将$r$加入旅程中即可)

    先保证思路1和2,$r$为重心当且仅当$sz_{max}le sum sz_{other}+1$,而当$sum sz_{other}<sz_{max}$时,可以将另外的子树合并起来,从$sz_{max}$开始(否则会不够用),依次修改这颗子树中和其他子树中的点即可

    每一次修改,对于$sz_{max}-sum sz_{other}$的变化具有连续性,且最后一次修改必然不是在$sz_{max}$中(否则修改前也可以),因此通过这样的操作就可以保证思路1和2

    再保证思路3,构造方式是不断选择其他子树中最深的点,这样做的正确性证明如下:

    记$a_{i}$为第$i$次所选子树编号,$v_{i}$为第$i$次所选的点深度,那么若$a_{i}=a_{i+2}$则显然有$v_{i}ge v_{i+2}$,否则由于$a_{i} e a_{i+2}$,则$v_{i+1}ge v_{i+2}$(否则第$i+1$会选$a_{i+2}$),那么若$a_{i}<a_{i+2}le a_{i+1}$,不论$a_{i-1}=a_{i+2}或a_{i+1}$,另一个权值一定更大,而不会选$a_{i}$

    在$sum sz_{other}<sz_{max}$的过程中,对每一棵子树都选择最深的点,同奇偶的点必然是在同一棵子树中,因此满足该性质

    当两者交界时,记$x$表示选择$sz_{max}$的那一次,那么可能会有$v_{x-1}<v_{x+1}$,不满足思路3的要求

    考虑第$x-2$次操作,必然有$a_{x-2}=a_{x+1}$(否则可以选$v_{x+1}$),那么我们可以撤销这次操作,并直接进入交替选$sz_{max}$的过程,这样就有$sz_{max}=sum sz_{other}$,显然也是可行的

     1 #include "fun.h"
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 #define N 100005
     5 vector<int>v_son,ans,v[3];
     6 int r,n,sz[N],d[N];
     7 bool cmp(int x,int y){
     8     return d[x]<d[y];
     9 }
    10 bool pd(int x,int y,int z){
    11     int s=max(max(x,y),z);
    12     return x+y+z-s<s;
    13 }
    14 void push(int k){
    15     ans.push_back(v[k].back());
    16     v[k].pop_back();
    17 }
    18 vector<int> createFunTour(int nn,int q){
    19     n=nn;
    20     sz[r=0]=n;
    21     for(int i=1;i<n;i++){
    22         sz[i]=attractionsBehind(0,i);
    23         if ((n-sz[i]<=n/2)&&(sz[r]>sz[i]))r=i;
    24     }
    25     for(int i=0;i<n;i++)
    26         if (i!=r){
    27             d[i]=hoursRequired(r,i);
    28             if (d[i]==1)v_son.push_back(i);
    29         }    
    30     for(int i=0;i<n;i++){
    31         if (i==r)continue;
    32         bool flag=0;
    33         for(int j=1;j<v_son.size();j++)
    34             if ((i==v_son[j])||(hoursRequired(v_son[j],i)==d[i]-1)){
    35                 v[j].push_back(i);
    36                 flag=1;
    37                 break;
    38             }
    39         if (!flag)v[0].push_back(i);
    40     }
    41     for(int i=0;i<v_son.size();i++)sort(v[i].begin(),v[i].end(),cmp);
    42     if (v[0].size()<v[1].size())swap(v[0],v[1]);
    43     if (v[0].size()<v[2].size())swap(v[0],v[2]);
    44     for(int i=-1;;){
    45         if (pd(v[0].size(),v[1].size(),v[2].size()))break;
    46         if (i>=0)push(i);
    47         int p=i;
    48         i=-1;
    49         for(int j=0;j<v_son.size();j++)
    50             if ((j!=p)&&(v[j].size())&&((i<0)||(d[v[i].back()]<d[v[j].back()])))i=j;
    51     }
    52     if (v[0].size()<v[1].size())swap(v[0],v[1]);
    53     if (v[0].size()<v[2].size())swap(v[0],v[2]);
    54     for(int i=0;i<v[2].size();i++)v[1].push_back(v[2][i]);
    55     sort(v[1].begin(),v[1].end(),cmp);
    56     if ((ans.size())&&(v[1].size())&&(d[ans.back()]<d[v[1].back()])){
    57         v[1].push_back(ans.back());
    58         ans.pop_back();
    59         sort(v[1].begin(),v[1].end(),cmp);
    60     }
    61     while (v[0].size()){
    62         push(0);
    63         if (v[1].size())push(1);
    64     }
    65     ans.push_back(r);
    66     return ans;
    67 }
    View Code
  • 相关阅读:
    JTAG各类接口针脚定义及含义
    【转载】关于quartus ii软件中注释乱码问题的解决方法
    【转载】浅谈阻塞和非阻塞语句的本质区别
    labview事件结构学习
    Labview按钮的机械动作
    LabVIEW中数组的自动索引
    opencv实例三:播放AVI格式视频
    TensorRT 不支持Tensorflow的操作有如下
    深度学习模型stacking模型融合python代码,看了你就会使
    sklearn的K折交叉验证函数KFold使用
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/13781596.html
Copyright © 2020-2023  润新知