[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_{x\in L_{i}}w_{x}$,显然$L_{i}$两两不交且并集恰为点集$V$,再将其按照$W_{i}$从大到小排序(相同按照链顶深度从小到大排序),并在最后补上若干个$L_{i}=\empty$

结论1:若$S=\bigcup_{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}$链顶的祖先(严格),则$i\le j$

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

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

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

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

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

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

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

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

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

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

综上,来考虑原结论——

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

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

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


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

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

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

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

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

若$x\not\in 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 1\le i\le 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}$

(特别的,若$pos\ge 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判断$x\in S$,若$x\not\in S$再倍增找到其第一个排名$\le 2y-2$的祖先,将这个祖先到其路径上的点补上,并加上$\max(W_{2y-1}-$链剩余部分$,0)$即可

时间复杂度为$o(n\log n+q\log n)$,可以通过

[cf526G]Spiders Evil Plan
 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\n",ans);
91     }
92     return 0;
93 } 
View Code

 

上一篇:Git基本使用3:基本使用语法


下一篇:android 基于dex的插件化开发