• [cf526G]Spiders Evil Plan


    将其以$x$为根建树,并定义$k$的点权$w_{k}$为$k$到其父亲的边边权(特别的$w_{x}=0$),那么问题也可以看作选一个包含$x$的点集,满足其的导出子图连通且边集可以被划分为$y$条路径,并最大化点权和

    性质1:边集可以被划分为$y$条路径,当且仅当度为1的节点不超过$2y$个

    必要性:一条路径上至多有两个度为1的节点,那么$y$条路径也即至多有$2y$个

    充分性:将所有叶子按照dfs序排序,并将第$4k+0/1$个叶子向第$4k+2/3$个叶子匹配,考虑每一个子树内存在叶子的节点,相当于要求某个区间内的叶子存在向子树外的边,简单分析可以发现上述构造总存在这样的边,即合法

    对其长链剖分,定义长儿子为子树带权深度最深的儿子(多个任取),并以此将原树划分为若干条链

    记$L_{i}$为第$i$条链的点集且$W_{i}=sum_{xin L_{i}}w_{x}$,显然$L_{i}$两两不交且并集恰为点集$V$,再将其按照$W_{i}$从大到小排序(相同按照链顶深度从小到大排序),并在最后补上若干个$L_{i}=empty$

    结论1:若$S=igcup_{i=1}^{2y}L_{i}$的导出子图中$x$的度数不为1,则答案为$sum_{i=1}^{2y}W_{i}$;否则,设$k$为$S$中与$x$相邻的节点,则答案为$sum_{i=1}^{2y-1}W_{i}+W_{pos}$(其中$pos=min_{L_{i}cap sub_{k}=empty}i$)

    下面,来证明以下上述结论的正确性(或感性理解后直接跳至这里):

    引理1:若$L_{i}$链顶的父亲是$L_{j}$链顶的祖先(严格),则$ile j$

    性质2:存在一个取到最大点权和的点集$S$,使得$S$是若干个$L_{i}$的并

    任取一个取到最大点权和的点集$S$,若其不为若干个$L_{i}$的并,则必然存在$L_{i}$满足$L_{i} otsubseteq S$且$L_{i}cap S e empty$(注意到$Ssubseteq igcup L_{i}=V$),那么取其中最小的$i$,并做如下变换:

    令$x$为$L_{i}$中最深的点满足其在$S$中,并任取$x$子树中的一个叶子$y$,将$y$不断向上移动直至点$z$满足$z$有两个儿子$in S$或$z=x$,然后删除$y$到$z$路径上的点(包括$y$,不包括$z$),再选上$L_{i}$中$ otin S$的部分

    此时不改变度为1的节点数且点权和单调不降,因此$S$仍取到最大点权和且合法

    另一方面,影响的仅有$x$子树内,根据引理1显然不会影响$j<i$的$L_{j}$与$S$的关系,同时这使得$L_{i}subseteq S$,那么重复此过程即可使得$S$是若干个$L_{i}$的并

    引理2:若$S=igcup_{iin S'}L_{i}$且$forall iin S',L_{i} e empty$,则$S$中至少存在$|S'|$个度为1的点

    性质3:$forall kge 1,igcup_{i=1}^{k}L_{i}$的导出子图连通且除$x$以外恰有$k$个度为1的点

    归纳此结论成立,根据引理1$L_{1}$必然是包含$x$的链,那么显然成立

    考虑加入$L_{k+1}$,同样根据引理1$L_{k+1}$链顶的父亲必然要在$igcup_{i=1}^{k}L_{i}$中(否则必然在后面即矛盾),即证明其连通,同时也即链顶度数不为1,那么仅新增一个度为1的节点(链尾)

    (如果该父亲的度数为1,不难发现其必然是$x$,因此也不影响)

    综上,来考虑原结论——

    对于结论前半部分,性质2和引理2说明了答案上界为$sum_{i=1}^{2y}W_{i}$,同时根据性质3和条件($x$度数不为1)该上界可以被$igcup_{i=1}^{2y}L_{i}$取到,因此即为答案

    对于结论后半部分,注意到若选了$2y$条链,那么$x$的度数必然不能为1,再根据$pos$的最小性也即链不能都选在$L_{pos}$之前,因此$sum_{i=1}^{2y-1}W_{i}+W_{pos}$为答案上界且可以被$igcup_{i=1}^{2y-1}L_{i}cup L_{pos}$取到,即为答案

    但$x$并不固定,因此长链的划分也不固定,需要每次询问重新建树,无法通过


    考虑优化,任取一条(带权)直径$(a,b)$,不妨假设其中$a$距离$x$较远,那么根据直径的性质,不难得到$a$也是距离$x$最远的点(之一)

    引理3:存在一个取到最大点权和的点集$S$,使得$ain S$

    由此,问题可以看作$x=a$且强制选择$x$的答案

    若没有选择$x$的限制,根据结论1的构造答案集合可以为$S=igcup_{i=1}^{2y-1}L_{i}$(注意$a$一定是叶子)

    若$xin S$,即$S$本身也满足条件,那么显然取到最大,答案也即$sum_{i=1}^{2y-1}W_{i}$

    若$x otin S$,强制选择$x$可以看作将$x$的点权加上$infty$,那么长儿子的变化即将$x$到$a$路径上(包括$a$,不包括$x$)的点长儿子替换为其链上的儿子,长链的变化也即将其与$x$到$a$路径上的交删除(并新增一条长链)

    (注意这里加上$infty$只是为了方便理解长儿子/长链形式上的变化)

    由于$x$的点权足够大,一定会先选上新增的这条链,并再选此时最长的$2y-2$条链即可

    性质4:对于原来$L_{1},L_{2},..,L_{2y-2}$,仅有$x$到$a$路径有交且链顶最深的$L_{pos}$可能被"顶替"

    令$L'_{i}$为变化后的$L_{i}$,$W'_{i}$为对应权值,由于只有删除,即$W'_{i}le W_{i}$,不难得到$forall 1le ile 2y-2$不被"顶替"的充分条件是$W_{i}=W'_{i}$或$W'_{i}ge W_{2y-2}$

    进而考虑$W_{i}>W'_{i}$的链,即其与$x$到$a$路径有交,令$x$为$L'_{i}$链顶的父亲,在长儿子变化之前$L'_{i}$的链顶即是$x$的长儿子,同时$x$显然也是$L_{pos}$的链顶的祖先,因此$W'_{i}ge W_{pos}ge W_{2y-2}$

    (特别的,若$posge 2y-1$,即其中不存在$W_{i}>W'_{i}$的链)

    进而考虑替换$L_{pos}$的链,如果$L_{2y-1}$与$x$到$a$路径没有交显然是$L_{2y-1}$

    若$L_{2y-1}$与$x$到$a$路径有交,类似上述证明一定有$W'_{pos}ge W_{2y-1}$,即剩下的链都无法替换$L_{pos}$,因此不妨直接比较$W'_{pos}$和$W_{2y-1}$并选择即可

    具体实现中,需要维护以下信息:

    1.每一条长链的链顶、链尾和点权(前缀)和

    2.每一个点深度和所在链的排名

    3.倍增维护其到其$2^{i}$祖先这条路径上排名最低的点(指所在链排名)的排名

    由此,先根据信息2判断$xin S$,若$x otin S$再倍增找到其第一个排名$le 2y-2$的祖先,将这个祖先到其路径上的点补上,并加上$max(W_{2y-1}-$链剩余部分$,0)$即可

    时间复杂度为$o(nlog n+qlog n)$,可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 100005
     4 struct Edge{
     5     int nex,to,len;
     6 }edge[N<<1];
     7 int n,m,E,x,y,z,ans,head[N],dep[N],tmp[N];
     8 struct Chain{
     9     int top,tail,len;
    10     bool operator < (const Chain &k)const{
    11         return (len>k.len)||(len==k.len)&&(tmp[len]<tmp[k.len]);
    12     }
    13 };
    14 void add(int x,int y,int z){
    15     edge[E]=Edge{head[x],y,z};
    16     head[x]=E++;
    17 }
    18 void dfs(int k,int fa,int s){
    19     dep[k]=s;
    20     for(int i=head[k];i!=-1;i=edge[i].nex)
    21         if (edge[i].to!=fa)dfs(edge[i].to,k,s+edge[i].len);
    22 }
    23 struct Data{
    24     int rt,w[N],dep[N],leaf[N],bl[N],sum[N],f[N][20],mn[N][20];
    25     vector<Chain>v;
    26     void dfs(int k,int fa,int s){
    27         w[k]=s-dep[fa],dep[k]=s,f[k][0]=fa;
    28         for(int i=1;i<20;i++)f[k][i]=f[f[k][i-1]][i-1];
    29         leaf[k]=k;
    30         for(int i=head[k];i!=-1;i=edge[i].nex)
    31             if (edge[i].to!=fa){
    32                 dfs(edge[i].to,k,s+edge[i].len);
    33                 int x=leaf[edge[i].to];
    34                 if ((!leaf[k])||(dep[leaf[k]]<dep[x]))leaf[k]=x;
    35             }
    36         for(int i=head[k];i!=-1;i=edge[i].nex)
    37             if ((edge[i].to!=fa)&&(leaf[edge[i].to]!=leaf[k])){
    38                 int x=edge[i].to,y=leaf[edge[i].to];
    39                 v.push_back(Chain{x,y,dep[y]-dep[f[x][0]]});
    40             }
    41     }
    42     void get_mn(int k,int fa){
    43         mn[k][0]=bl[k];
    44         for(int i=1;i<20;i++)mn[k][i]=min(mn[k][i-1],mn[f[k][i-1]][i-1]);
    45         for(int i=head[k];i!=-1;i=edge[i].nex)
    46             if (edge[i].to!=fa)get_mn(edge[i].to,k);
    47     }
    48     void build(int k){
    49         rt=k;
    50         dfs(rt,0,0);
    51         v.push_back(Chain{rt,leaf[rt],dep[leaf[rt]]});
    52         memcpy(tmp,dep,sizeof(tmp));
    53         sort(v.begin(),v.end());
    54         for(int i=0;i<v.size();i++){
    55             sum[i+1]=sum[i]+v[i].len;
    56             for(int j=v[i].tail;j!=v[i].top;j=f[j][0])bl[j]=i+1;
    57             bl[v[i].top]=i+1;
    58         }
    59         get_mn(rt,0);
    60     }
    61     int query(int x,int y){
    62         if (bl[x]<(y<<1))return sum[min((y<<1)-1,(int)v.size())];
    63         int z=x;
    64         for(int i=19;i>=0;i--)
    65             if (mn[z][i]>(y<<1)-2)z=f[z][i];
    66         if (!z)return sum[(y<<1)-2]+(dep[leaf[x]]-dep[z]);
    67         return sum[(y<<1)-2]+(dep[leaf[x]]-dep[z])+max(v[(y<<1)-2].len-(dep[v[bl[z]-1].tail]-dep[z]),0);
    68     }
    69 }Ta,Tb;
    70 int main(){
    71     scanf("%d%d",&n,&m);
    72     memset(head,-1,sizeof(head));
    73     for(int i=1;i<n;i++){
    74         scanf("%d%d%d",&x,&y,&z);
    75         add(x,y,z),add(y,x,z);
    76     }
    77     dfs(1,0,0);
    78     x=y=1;
    79     for(int i=2;i<=n;i++)
    80         if (dep[x]<dep[i])x=i;
    81     dfs(x,0,0);
    82     for(int i=2;i<=n;i++)
    83         if (dep[y]<dep[i])y=i;
    84     Ta.build(x),Tb.build(y);
    85     for(int i=1;i<=m;i++){
    86         scanf("%d%d",&x,&y);
    87         x=(x+ans-1)%n+1,y=(y+ans-1)%n+1;
    88         if (Ta.dep[x]>Tb.dep[x])ans=Ta.query(x,y);
    89         else ans=Tb.query(x,y);
    90         printf("%d
    ",ans);
    91     }
    92     return 0;
    93 } 
    View Code
  • 相关阅读:
    【20220204】连岳摘抄
    【20220208】学会照顾自己,是更大的责任
    【20220205】连岳摘抄
    【20220209】逆向思考的终极解救
    【20220202】连岳摘抄
    【20220207】重新找回节假日
    【20220203】连岳摘抄
    从Hadoop Writable序列化框架到java的序列化原理
    Hadoop Configuration配置类的分析
    从Hadoop Writable序列化框架到java的序列化原理
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15358698.html
Copyright © 2020-2023  润新知