首页 > 其他分享 >校门外歪脖树上的鸽子

校门外歪脖树上的鸽子

时间:2023-07-23 21:44:42浏览次数:22  
标签:新树 鸽子 int MAX 门外 mid Fa lca 树上

思路很简单,但非常考验代码能力

思路

假设对区间[2,8]进行操作

路径由lca 15分成左右俩条链。将左端点2跳到最右上17,对左链17-16-10-15上每个作为左儿子的节点的兄弟操作。

于是构造新树:节点的新父亲为最近异侧祖先的同侧儿子

(手画略丑请忽略)

链上区间操作——树链剖分

一共有三棵树:题目所给原树,构造新树,树链剖分所用线段树

来整合一下需要的操作,便于理清代码思路,并代替行间注释

1、dfs()遍历原树 cnt[]子树所含叶子数,即节点包含区间长度  dep[]深度  Fa[][]倍增求lca

2、Do()连边构造新树

3、树链剖分fhe() Son[]重儿子  siz[]新树子树大小  fa[]新树父亲

      che() s[]新树cnt前缀和  dfn[]  top[]

4、操作

getlca()

L,R跳到同侧最高

如果L,R都在lca之上,对lca操作,return

如果L在lca之上,对lca左儿子操作,return

树剖[L,lca右儿子)

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e6+10;
#define int long long
int n,q,x,y,d,op,fa[MAX],son[MAX][2],rt,bro[MAX];
int cnt[MAX],s[MAX];
int Son[MAX],siz[MAX],dfn[MAX],chuo,top[MAX],dep[MAX],Fa[MAX][20]; 
vector<int>g[MAX];
struct node{
    int sum,laz;
} t[MAX<<1];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
void dfs(int,int);
void Do(int,int,int);
inline int getlca(int,int);
void fhe(int,int);
void che(int,int);
inline void add(int,int,int,int);
inline int ask(int,int,int); 
void update(int,int,int,int,int,int);
int query(int,int,int,int,int);
inline void pushdown(int,int,int);
signed main(){
    n=read();q=read();
    for(int i=1+n;i<2*n;++i){
        son[i][0]=read();son[i][1]=read();
        bro[son[i][0]]=son[i][1];bro[son[i][1]]=son[i][0];
    }for(int i=n+1;i<2*n;++i)
        if(!bro[i]){rt=i;break;}
    son[0][0]=son[0][1]=rt;bro[rt]=rt;
    dfs(rt,rt);Do(rt,0,0);Do(rt,0,1);
    fhe(rt,rt);che(rt,rt);
    for(int i=2;i<=chuo;++i)  s[i]+=s[i-1]; 
    while(q--){
        op=read();x=read();y=read();
        int lca=getlca(x,y);//
        if(x==son[Fa[x][0]][0])  x=bro[fa[x]];
        if(y==son[Fa[y][0]][1])  y=bro[fa[y]];
        if(op==1){
            d=read();
            if(dep[x]<=dep[lca]&&dep[y]<=dep[lca]){
                update(1,1,2*n-1,dfn[lca],dfn[lca],d);continue;
            }add(x,lca,d,0);add(y,lca,d,1);
                
        }else{
            if(dep[x]<=dep[lca]&&dep[y]<=dep[lca]){
                printf("%lld\n",query(1,1,2*n-1,dfn[lca],dfn[lca]));
                continue;
            }printf("%lld\n",ask(x,lca,0)+ask(y,lca,1));
        }
    }
}
void dfs(int u,int f){
    dep[u]=dep[f]+1;Fa[u][0]=f;
    for(int i=1;(1<<i)<=dep[u];++i)  Fa[u][i]=Fa[Fa[u][i-1]][i-1];
    if(!son[u][0]){cnt[u]=1;return;}
    dfs(son[u][0],u);dfs(son[u][1],u); 
    cnt[u]=cnt[son[u][0]]+cnt[son[u][1]];
}void Do(int u,int x,int p){
    if(!u)  return;
    if(u==son[Fa[u][0]][p])  g[son[x][p]].push_back(u);
    Do(son[u][p],x,p);Do(son[u][p^1],u,p);
}inline int getlca(int x,int y){
    if(dep[x]<dep[y])  swap(x,y);
    int h=dep[x]-dep[y];
    for(int i=0;i<20;++i)  
        if(h&(1<<i))  x=Fa[x][i];
    if(x==y)  return x;
    for(int i=19;i>=0;--i)
        if(Fa[x][i]!=Fa[y][i]){
            x=Fa[x][i];y=Fa[y][i];
        }
    return Fa[x][0];
} 
void fhe(int u,int f){
    fa[u]=f;siz[u]=1;
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        fhe(v,u);siz[u]+=siz[v];
        if(siz[Son[u]]<siz[v])  Son[u]=v; 
    }
}void che(int u,int ance){
    dfn[u]=++chuo;top[u]=ance;s[chuo]=cnt[u];
    if(Son[u])  che(Son[u],ance);
    for(int i=0;i<g[u].size();++i)
        if(g[u][i]!=Son[u])  che(g[u][i],g[u][i]);
}inline void add(int x,int y,int num,int p){
    if(dep[x]<=dep[y]){
        update(1,1,2*n-1,dfn[son[y][p]],dfn[son[y][p]],num);
        return;
    }y=son[y][p^1];
    while(top[x]!=top[y]){
        update(1,1,2*n-1,dfn[top[x]],dfn[x],num);
        x=fa[top[x]];
    }update(1,1,2*n-1,dfn[y]+1,dfn[x],num);
}inline int ask(int x,int y,int p){
    if(dep[x]<=dep[y])  
        return query(1,1,2*n-1,dfn[son[y][p]],dfn[son[y][p]]);
    y=son[y][p^1];int ans=0;
    while(top[x]!=top[y]){
        ans+=query(1,1,2*n-1,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }ans+=query(1,1,2*n-1,dfn[y]+1,dfn[x]);
    return ans;
} 
void update(int pos,int l,int r,int ll,int rr,int d){
    if(l>=ll&&r<=rr){
        t[pos].sum+=(s[r]-s[l-1])*d;
        t[pos].laz+=d;return;//
    }int mid=l+r>>1;pushdown(pos,s[mid]-s[l-1],s[r]-s[mid]);
    if(mid>=ll)  update(pos<<1,l,mid,ll,rr,d);
    if(mid<rr)  update(pos<<1|1,mid+1,r,ll,rr,d);
    t[pos].sum=t[pos<<1].sum+t[pos<<1|1].sum;
}int query(int pos,int l,int r,int ll,int rr){
    if(l>rr||r<ll)  return 0;
    if(l>=ll&&r<=rr)  return t[pos].sum;
    int mid=l+r>>1;pushdown(pos,s[mid]-s[l-1],s[r]-s[mid]); 
    return query(pos<<1,l,mid,ll,rr)+query(pos<<1|1,mid+1,r,ll,rr);
}inline void pushdown(int pos,int l1,int l2){
    if(t[pos].laz){
        t[pos<<1].sum+=l1*t[pos].laz;t[pos<<1|1].sum+=l2*t[pos].laz;
        t[pos<<1].laz+=t[pos].laz;t[pos<<1|1].laz+=t[pos].laz;
        t[pos].laz=0;
    }
}
View Code

因为线段树懒标记没有+=,调了一下午,原地去世

标签:新树,鸽子,int,MAX,门外,mid,Fa,lca,树上
From: https://www.cnblogs.com/yswn/p/17575955.html

相关文章

  • 【LuoGU 1273】有线电视网——树上分组背包问题
    有线电视网题目描述某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总......
  • 树上启发式合并学习笔记
    树上启发式合并\((dsu\on\tree)\)适用条件:可以在一个子树内统计的问题,并且不带修改。暴力复杂度一般为\(O(n^2)\)。例题:CF600ELomsatgelral解法考虑一个问题,给你一棵树,每个节点有一个颜色,如果一种颜色在以\(x\)为根的子树内出现次数最多,则称\(col_i\)占主要色。......
  • 浅谈树上问题
    树的直径定义规定树上任意两节点之间的最远距离为树的直径解法较为主流的解法有两种贪心以任意节点\(x\)为根进行一次\(\text{DFS}\),记录距\(x\)最远的节点编号为\(y\),再以\(y\)为根进行第二次\(\text{DFS}\),得到距\(y\)最远的节点编号\(z\),那么\(dis(x,......
  • BZOJ #3784. 树上的路径
    BZOJ#3784.树上的路径题意给一颗树,求所有路径长度中前\(k\)大。题解首先对于前\(k\)大,我们有一个常见的方法,二分。二分第\(k\)大的路径长度,然后使用点分治统计,点分治内部还要二分,所以时间复杂度\(O(nolg^3n)\)。二分显然是行不通了,想一下就会发现外层和内层的二分......
  • abc070d <简单树上dfs>
    D-TransitTreePath//https://atcoder.jp/contests/abc070/tasks/abc070_d//<简单树上dfs>#include<iostream>#include<algorithm>#include<vector>usingnamespacestd;usingLL=longlong;constintN=1e5+10;structNode{......
  • 树上dp
    树上dp树的存储邻接表:将这个点的所有直接子节点存储在以这个点为开头的链表上https://oi-wiki.org/graph/save/#邻接表voidadd(intu,intv)//添加一条边u->v{cnt++;nxt[cnt]=head[u];head[u]=cnt;to[cnt]=v;}voidsolve(intu){......
  • codeforces 树上题目总结
    codeforces树上题目总结CF1559D2先猜一个结论——一定能通过加边让一个森林变成一棵树,归纳一下发现是对的,并且随便加合法的边都符合条件,所以暴力是\(\mathcalO(n^2)\)的。那么考虑如何降低复杂度。我并没有想到。我一直是在往快速找到两个不属于同一集合的点这个方向思考的......
  • 关于鸽子
    个人介绍放那么多东西有什么用啊个人介绍搞那么的干净有什么用啊简介一名现役OIer,坐标HNFMS,唤做醉酒拳皇的就是了。UPD:现在组里的假人叫我帝王,有抽象老哥叫我电网。可能看到的其他ID或外号:Danewol(CF上用的,原ID被抢了),华枭逸锋,湖宫梵也。最大的爱好是打游戏,尤其对......
  • 记一次Android奇葩面试经历:因为没去过BAT,我被面试官“轰”出门外
    最近面试了几家大规模的公司,也遇到了各种各种的问题,技术方面的,管理方面的都有涉及。让我印象最深刻的是某上市公司,自称是阿里的控股子公司,创始人团队来自于阿里,感觉很高大上的样子。进门之后就是填表,然后就是技术负责人面试,问了一些项目中的问题。有的没的扯一大堆,对技术不是很看中......
  • 洛谷P4178 Tree 题解 树上点分治
    题目链接:https://www.luogu.com.cn/problem/P4178解题思路:点分治模板题。设当前重心为\(u\),一共有三种不同类型的路径:路径的一个端点恰好是重心\(u\);路径的两个端点在\(u\)的不同子树中;路径的两个端点在\(u\)的同一个子树中。找到重心\(u\)之后,前两类路径分开求......