首页 > 其他分享 >题解 P6329

题解 P6329

时间:2023-07-17 22:14:16浏览次数:38  
标签:pre int 题解 P6329 fa lst 节点 dis

点分树模板题。是个神奇的算法。

点分树就是将分治重心按照层级连边,每个节点父亲的联通块都包含了当前节点的联通块,且高度为 \(\log n\)。可以解决不考虑树的形态的多次询问带修改的问题。

对于这道题,我们可以在点分树的每个节点上记录两棵树状数组,分别与当前节点距离为 \(k\) 的节点的权值和,以及与当前节点父亲距离为 \(k\) 的权值和。

这里的 LBT 要用 vector 存,空间复杂度 \(O(n\log n)\)。

查询的时候直接从点分树上对应的节点往上跳,一直到根。

加入当前节点 \(u\) 与 \(x\) 的距离为 \(dis\),那么 \(u\) 的贡献为 \(sum_1[u][k-dis]-sum_2[u][k-dis]\)。需要减掉算重复的。

修改直接对根到 \(x\) 的链上的每个点改即可。

因为查询时 \(C_1,C_2\) 对应的范围相等,所以空间可以都开成联通块的大小。

复杂度 \(O(n\log^2n)\)。

code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,inf=INT_MAX>>1;
int n,m,val[N],vis[N],f[N],tot,siz[N],sz[N],rt,fa[N][22],pre[N],dep[N];
vector<int>adj[N],c[2][N];
void getg(int u,int lst){
  siz[u]=1;f[u]=0;
  for(int i=0;i<adj[u].size();++i){
    int v=adj[u][i];if(v==lst||vis[v])continue;
    getg(v,u);siz[u]+=siz[v];f[u]=max(f[u],siz[v]);
  }
  f[u]=max(f[u],tot-siz[u]);
  if(f[u]<f[rt])rt=u;
}
int build(int u,int s){
  rt=0;tot=s;getg(u,0);
  int g=rt;
  vis[g]=1;sz[g]=s;
  for(int i=0;i<adj[g].size();++i){
    int v=adj[g][i],tmp;if(vis[v])continue;
    tmp=build(v,s-f[v]);pre[tmp]=g;
  }
  return g;
}
void dfs(int u,int lst){
  fa[u][0]=lst;dep[u]=dep[lst]+1;
  for(int i=1;i<=20;++i)fa[u][i]=fa[fa[u][i-1]][i-1];
  for(int i=0;i<adj[u].size();++i){
    int v=adj[u][i];if(v==lst)continue;
    dfs(v,u);
  }
}
int lca(int u,int v){
  if(dep[u]<dep[v])swap(u,v);
  for(int i=20;i>=0;--i)if(dep[fa[u][i]]>=dep[v])u=fa[u][i];
  if(u==v)return u;
  for(int i=20;i>=0;--i)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
  return fa[u][0];
}
int lbt(int x){return x&(-x);}
void update(int id,int u,int i,int k){++i;for(;i<c[id][u].size();i+=lbt(i))c[id][u][i]+=k;}
int getsum(int id,int u,int i){int res=0;++i;int tmp=c[id][u].size()-1;i=min(i,tmp);for(;i;i-=lbt(i))res+=c[id][u][i];return res;}
int getdis(int u,int v){return dep[u]+dep[v]-2*dep[lca(u,v)];}
void modify(int u,int w){
  for(int i=u;i;i=pre[i])update(0,i,getdis(i,u),w);
  for(int i=u;pre[i];i=pre[i])update(1,i,getdis(pre[i],u),w);
}
int main(){
  //freopen("test.out","w",stdout);
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  cin>>n>>m;
  for(int i=1;i<=n;++i)cin>>val[i];
  for(int i=1;i<n;++i){
    int u,v;cin>>u>>v;
    adj[u].push_back(v);adj[v].push_back(u);
  }
  f[0]=inf;
  build(1,n);dfs(1,0);
  for(int i=1;i<=n;++i)c[0][i].resize(sz[i]+2),c[1][i].resize(sz[i]+2);
  for(int i=1;i<=n;++i)modify(i,val[i]);
  int lst=0;
  while(m--){
    int op,x,y;cin>>op>>x>>y;x^=lst;y^=lst;
    if(op)modify(x,y-val[x]),val[x]=y;
    else{
      lst=getsum(0,x,y);
      for(int i=x;pre[i];i=pre[i]){
        int dis=getdis(pre[i],x);
        if(y>=dis){
          lst+=getsum(0,pre[i],y-dis)-getsum(1,i,y-dis);
        }
      }
      cout<<lst<<endl;
    }
  }
  return 0;
}

标签:pre,int,题解,P6329,fa,lst,节点,dis
From: https://www.cnblogs.com/HQJ2007/p/17561382.html

相关文章

  • 题解 P3345
    点分树。本题的核心问题在于不好直接确定补给站的位置。但是仔细思考后我们发现,对于当前节点,如果存在一个儿子的答案比它优,那么补给站一定在那个儿子的子树中。增量为\(w\times(sum_u-2\cdotsum_v)\)。当\(v\)优于\(u\)时,\(2\cdotsum_v>sum_u\)。如果\(v_2\)也满足,则......
  • 题解 P5384
    这题有紫??对于询问节点\(u\),倍增地跳到它的\(k\)级祖先,求其子树内有多少深度为\(dep_u\)的节点。我们考虑把询问离线,再通过dfs序把树拍平,然后扫一遍直接求就行了。复杂度\(O(n\logn)\)。code:#include<bits/stdc++.h>usingnamespacestd;constintN=1e6+5;inlin......
  • 题解 P3806
    点分治模板题。点分治适合处理大规模的树上路径信息问题暴力做法:dfs每个点\(u\),算出其子树内每个点到\(u\)的距离,统计经过\(u\)的所有路径,复杂度\(O(n^2)\)。容易发现,复杂度和子树大小有关。对于当前子树,我们可以求出其重心,计算经过重心的所有路径,删掉重心,递归每个联通......
  • 题解 CF1271D
    贪心+DP。对于一个点,后选显然比先选好,也就是说每个点都对应了唯一一个来源。于是我们可以把每个点所能回溯到的点的收益值从大到小排序,贪心地选前缀。定义\(f_{i,j}\)表示考虑了前\(i\)个点,剩下\(j\)个人,最大收益。转移方程和\(01\)背包的一样。\[f_{i,j}=f_{i-1,j-b......
  • 题解 CF213C
    考虑朴素DP。定义\(f_{i,j,i2,j2}\)表示两个人分别在\((i,j),(i2,j2)\)时获得的最大收益。复杂度\(O(n^4)\),不行。我们换种方法,定义\(f_{st,x,y}\)表示两人同时走了\(st\)步,分别向右走了\(x,y\)步。显然如果向右的步数确定了,向下的也确定了。转移方程如下:\[f_{st,x......
  • 题解 CF1265E
    期望DP。定义\(f_i\)表示第\(i\)个镜子照成功的期望天数,\(p_i\)为第\(i\)天成功的概率,\(q_i\)为第\(i\)天失败的概率。根据题意容易列出方程:\[f_i=(f_{i-1}+1)\cdotp_i+(f_{i-1}+1+f_i)\cdotq_i\]移项得:\[(1-q_i)\cdotf_i=(f_{i-1}+1)\cdot(p_i+q_i)\]同除以......
  • 题解 CF930C
    好题啊好题。定义\(a_i\)为有多少个区间包含\(i\)。拍脑袋一想,当且仅当存在顺序的三个坐标\((i,j,k)\)满足\(a_i>a_j\)且\(a_j<a_l\)时,可以确定没有数被所有区间包含。这个结论很简单,因为如果存在,则\(a\)序列必定为一个“山峰”。而如果出现上面这种情况,说明有“山......
  • 题解 P6772 [NOI2020] 美食家
    观察数据范围,\(T\)很大,\(n\)很小,用矩乘。对于一条边\((u,v,w)\),我们将\(u\)拆成\(w-1\)个点,并连接\((u_0,u_1,0),(u_1,u_2,0)...(u_{w-2},u_{w-1},0)\)和\((u_{w-1},v_0,c_{v})\),总点数\(5n\)。将美食节按时间排序,相邻两个美食节之间用矩阵快速幂转移,然后再加上附加......
  • 题解 CF1202C
    不错的题,需要点思维和码力。容易发现,左右和上下互不影响,可以分开处理,这里以左右举例。定义向左走一格\(-1\),向右走一格\(+1\),求个前缀和找到最大值和最小值,和出现最值的最早时间与最晚时间。定义为\(l,r,l2,r2\)。只有当我们放了一个A或D使得所有最大值\(-1\)且最小值......
  • 题解 P8338 [AHOI2022] 排列
    恶心题。每次操作,相当与把第\(i\)个数置换到\(p_i\),于是可以连边。因为\(i\)和\(p_i\)互不相同,所以对于每一个点,有且仅有一条出边和一条入边,即若干个简单环。那么最少操作\(\operatorname{lcm}(a_1,a_2,a_3...a_{x-2},a_{x-1},a_x)\)次点会都回到原位。其中\(a_i\)......