Problem

UOJ

Solution

问题的关键在于如何定位广义线段树上的一段区间 [ l , r ] [l,r] [l,r] 所对应的节点,可以考虑zkw线段树的方法,先找到 l − 1 l-1 l−1 和 r + 1 r+1 r+1 所对应的节点,并且向上跳直到它们的lca处, l − 1 l-1 l−1 祖先的所有右儿子以及 r − 1 r-1 r−1 祖先的所有左儿子都是对应的节点。需要注意的是为了提取 l = 1 l=1 l=1 或 r = n r=n r=n 的区间,我们需要加哨兵节点,只需要在原树的顶上多接4个节点即可。

不难发现我们每次都是找到了原树中一条链的所有右儿子和另一条链的所有左儿子。我们可以把链上连续的左儿子连接起来,得到一棵新树,不妨称为左树,同样地,我们可以得到右树。

我们不加证明地给出两棵树一些性质:

  • 两棵树没有交集
  • 左树上一个节点的父亲是 / 原树上最近的一个是右儿子的祖先 / 的兄弟节点,右树类似
  • 此时左树会以 0 0 0 表示的叶子为根,右树会以 n + 1 n+1 n+1 表示的叶子为根
  • 在定位区间时,我们需要找到 l − 1 l-1 l−1 和 r + 1 r+1 r+1 的左右链的起始位置,讨论一下可以得到:
    • 左儿子的左链起始于左树上的父亲,右链起始于兄弟
    • 右儿子的右链起始于右树上的父亲,左链起始于兄弟

我们用一个虚拟的根把两棵树并在一起,这样每次询问的点集就是新树上的两条路径。注意到答案是可以差分的,把询问拆成两个,询问的点集就必然是一个点到根的路径了。

怎么求一个点到根路径上的点集呢?可以联想到树上莫队的方法,用dfs序记录一个节点的进出,那么 x x x 进入时的前缀就表示了这个点集。现在问题变成了:向关键点集中加入/删除一个元素,询问一个点到关键点集的树上距离和。

把距离表达式拆开,然后用树剖+树状数组即可做到 O ( ( n + m ) log ⁡ 2 n ) O((n+m)\log ^2 n) O((n+m)log2n)。

Code

实现的体验不怎么好,还好细节不太多,没调太久。刚开始打完了树剖发现只写了3.1k,感觉快写完了,还以为自己写得好短,结果后来发现忘了写倍增,原树的一些信息没有初始化……不知不觉就4.4k了

写之前没好好构思排版,写得过程中处理边界情况有点难受,担心思路混乱就代码写得丑了点,现在写完不想改了。。所以就这么长吧,好像是今年写过最长的题了

#include <bits/stdc++.h>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int maxn=400010;
template <typename Tp> int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> void read(Tp &x)
{x=0;char ch=getchar();int f=0;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=1,ch=getchar();while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();if(f) x=-x;
}
int n,m,rt,tot,qc,now,seq[maxn<<1],dfn[maxn],dep[maxn];
int mid[maxn],lc[maxn],rc[maxn],nxt[maxn],L[maxn],R[maxn],lst[maxn],rst[maxn];
ll sd,ans[maxn];
struct Query{int pos,x,f,id;bool operator < (const Query &b)const{return pos<b.pos;}
}q[maxn<<1];
namespace Tree{struct data{int v,nxt;}edge[maxn];int tot,p,head[maxn],f[18][maxn];void insert(int u,int v){edge[++p]=(data){v,head[u]};head[u]=p;}int in(int a,int b,int c,int d){return a<=c&&d<=b;}//[c,d] in [a,b]?void dfs(int x){seq[++tot]=x;dfn[x]=tot;for(int i=1;i<18;i++) f[i][x]=f[i-1][f[i-1][x]];for(int i=head[x];i;i=edge[i].nxt){f[0][edge[i].v]=x;dfs(edge[i].v);}seq[++tot]=-x;}int ql(int x,int l,int r){if(!in(l,r,L[x],R[x])) return x;for(int i=17;~i;i--)if(f[i][x]&&in(l,r,L[f[i][x]],R[f[i][x]]))x=f[i][x];return f[0][x];}int qr(int x,int l,int r){if(!in(l,r,L[x],R[x])) return x;for(int i=17;~i;i--)if(f[i][x]&&in(l,r,L[f[i][x]],R[f[i][x]]))x=f[i][x];return f[0][x];}
}
using Tree::insert;
namespace TCL{int dfc,f[maxn],sz[maxn],hs[maxn],top[maxn],pos[maxn];struct BIT{ll a[maxn],a2[maxn];void add(int p,int v){for(int i=p;i<=tot;i+=lowbit(i)) a[i]+=v,a2[i]+=(ll)p*v;}ll qsum(int p){ll res=0ll;for(int i=p;i;i-=lowbit(i)) res+=(ll)(p+1)*a[i]-a2[i];return res;}}T;void dfs1(int x){sz[x]=1;if(!lc[x]) return ;f[lc[x]]=f[rc[x]]=x;dfs1(lc[x]);sz[x]+=sz[lc[x]];dfs1(rc[x]);sz[x]+=sz[rc[x]];hs[x]=sz[lc[x]]<sz[rc[x]]?rc[x]:lc[x];}void dfs2(int x,int s){top[x]=s;pos[x]=++dfc;if(!lc[x]) return ;if(hs[x]) dfs2(hs[x],s);if(lc[x]^hs[x]) dfs2(lc[x],lc[x]);if(rc[x]^hs[x]) dfs2(rc[x],rc[x]);}void update(int x,int v){for(int fx=top[x];x;x=f[fx],fx=top[x])T.add(pos[fx],v),T.add(pos[x]+1,-v);}ll query(int x){ll res=0ll;for(int fx=top[x];x;x=f[fx],fx=top[x])res+=T.qsum(pos[x])-T.qsum(pos[fx]-1);return res;}
}
using TCL::update;
using TCL::query;
void build(int l,int r,int &rt)
{rt=++tot;L[rt]=l;R[rt]=r;if(l==r) return ;read(mid[rt]);build(l,mid[rt],lc[rt]);build(mid[rt]+1,r,rc[rt]);nxt[lc[rt]]=rc[rt];nxt[rc[rt]]=lc[rt];
}
void dfs(int x,int ls,int rs,int l)//ls stands for the nearest anc which is leftson
{if(x<(n<<1)) l?insert(nxt[rs],x):insert(nxt[ls],x);if(!lc[x]){if(L[x]==0){lst[L[x]]=rst[L[x]]=x;return ;}if(L[x]==n+1){lst[L[x]]=1;rst[L[x]]=x;return ;}if(l){lst[L[x]]=nxt[rs];rst[L[x]]=nxt[x];}else{lst[L[x]]=nxt[x];rst[L[x]]=nxt[ls];}return ;}dep[lc[x]]=dep[rc[x]]=dep[x]+1;if(l){dfs(lc[x],x,rs,1);dfs(rc[x],x,rs,0);}else{dfs(lc[x],ls,x,1);dfs(rc[x],ls,x,0);}
}
void input()
{int x,l,r,lx,lf,rx,rf;read(n);build(1,n,rt);//deal with extra nodes initial informationlc[tot+2]=tot+1;rc[tot+2]=tot+3;lc[tot+3]=rt;rc[tot+3]=tot+4;L[tot+1]=R[tot+1]=0;L[tot+2]=0;R[tot+2]=n+1;L[tot+3]=1;R[tot+3]=n+1;L[tot+4]=R[tot+4]=n+1;nxt[tot+1]=tot+3;nxt[tot+3]=tot+1;nxt[1]=tot+4;nxt[tot+4]=1;insert(0,tot+1);insert(0,tot+4);rt=tot+2;tot+=4;dep[rt]=1;//build another tree and use its DFN to get ansdfs(rt,0,0,1);Tree::dfs(0);TCL::dfs1(rt);TCL::dfs2(rt,rt);//divide query into four parts read(m);for(int i=1;i<=m;i++){read(x);read(l);read(r);lx=rst[l-1];rx=lst[r+1];lf=Tree::ql(lx,l,r);rf=Tree::qr(rx,l,r);q[++qc]=(Query){dfn[lx],x,1,i};q[++qc]=(Query){dfn[lf],x,-1,i};q[++qc]=(Query){dfn[rx],x,1,i};q[++qc]=(Query){dfn[rf],x,-1,i};}sort(q+1,q+qc+1);
}
int main()
{input();for(int i=1,j=1;i<=Tree::tot;i++){if(seq[i]){update(abs(seq[i]),seq[i]>0?1:-1);seq[i]>0?++now:--now;seq[i]>0?sd+=dep[seq[i]]:sd-=dep[-seq[i]];}for(;j<=qc&&q[j].pos<=i;j++)ans[q[j].id]+=q[j].f*(sd+(ll)now*dep[q[j].x]-2ll*query(q[j].x));}for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);return 0;
}

UOJ295 ZJOI2017 线段树相关推荐

  1. 二逼平衡树——树套树(线段树套Splay平衡树)

    题面 Bzoj3196 解析 线段树和Splay两棵树套在一起,常数直逼inf,但最终侥幸过了 思路还是比较简单, 在原数组维护一个下标线段树,再在每一个线段树节点,维护一个对应区间的权值Splay. ...

  2. 线段树——HDU - 1698

    题目含义 就是初始化一堆数为1 可以经过操作把一个区间的数都改变 并求这堆数的总大小 题目分析 有一个 #include<iostream> #include<stdio.h> ...

  3. BZOJ.1558.[JSOI2009]等差数列(线段树 差分)

    BZOJ 洛谷 首先可以把原序列\(A_i\)转化成差分序列\(B_i\)去做. 这样对于区间加一个等差数列\((l,r,a_0,d)\),就可以转化为\(B_{l-1}\)+=\(a_0\),\(B ...

  4. 【线段树分治 线性基】luoguP3733 [HAOI2017]八纵八横

    不知道为什么bzoj没有HAOI2017 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路 ...

  5. [bzoj1582][Usaco2009 Hol]Holiday Painting 节日画画_线段树

    Holiday Painting 节日画画 bzoj-1582 Usaco-2009 Hol 题目大意:给定两个n*m的01网格图.q次操作,每次将第二个网格图的子矩阵全部变成0或1,问每一次操作后两 ...

  6. codefores 786B. Legacy(最短路,线段树优化拆点,好题)

    题目链接 B. Legacy time limit per test2 seconds memory limit per test256 megabytes inputstandard input o ...

  7. 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树

    题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...

  8. 树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)

    题目链接 题意: 给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径(最短)上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一 ...

  9. bzoj1095: [ZJOI2007]Hide 捉迷藏 线段树维护括号序列 点分治 链分治

    这题真是十分难写啊 不管是点分治还是括号序列都有一堆细节.. 点分治:时空复杂度$O(n\log^2n)$,常数巨大 主要就是3个堆的初始状态 C堆:每个节点一个,为子树中的点到它父亲的距离的堆. B ...

最新文章

  1. HTML5 元素拖拽实现 及 jquery.event.drag插件
  2. 针对BCH阵营对立,BU开发组提出投票妥协策略
  3. redhat7防火墙关闭_RedHat Enterprise Linux 7关闭防火墙方法
  4. perl计算IP所在的子网范围
  5. (3) 用java编译器实现一个简单的编译器-语法分析
  6. 操作系统:升级Windows 11正式版的四种方法,值得收藏!
  7. mysql 10分钟一聚合_SQL-根据DateTime查询多个聚合-MySQL
  8. TS字面量进行类型声明
  9. C++语言基础 —— STL —— 容器与迭代器 —— pair
  10. spring boot实现WebMvcConfigurer接口定制SpringMvc配置
  11. c语言程序设计 a b,用C语言编程时,a+=b 和 a=a+b 真的完全等价吗?
  12. Linux---网络编程基础
  13. Linux下conda常用命令整理
  14. “色情机器”改掉手机PIN码 勒索安卓设备用户
  15. 在vue项目中调用echarts官网百度地图实例
  16. iso硬盘安装 凤凰os_在Linux中安装凤凰系统(Phoenix OS)的方法
  17. javascript+css实现走马灯图片轮播器
  18. Android仿微信朋友圈5实现朋友圈列表
  19. C++源代码单词扫描程序(词法分析)
  20. 求树的直径算法以及证明

热门文章

  1. 教你炒股票7:给-赚-了指数亏-了-钱的一些忠告
  2. JS面向对象——利用transform实现扇子效果
  3. SlimScroll插件学习
  4. 为什么你的程序总是出现 bug?
  5. ai怎么切图制作html,APP扁平化设计最强AI切图工具推荐与AI切图教程
  6. 39个家宽用户屋内wifi网速调查
  7. UI自动化基础 - selenium快速入门教学
  8. 联想 n700 android,联想N700无限鼠标 一个深沉低调的存在
  9. 微信小程序下载保存文件
  10. 5G LAN赋能智慧工厂加速落地,四信5G工业路由器成最佳助攻