「HNOI2016」树 解题报告
「HNOI2016」树
事毒瘤题...
我一开始以为每次把大树的子树再接给大树,然后死活不知道咋做,心想怕不是个神仙题哦
然后看题解后才发现是把模板树的子树给大树,虽然思维上难度没啥了,但是还是很难写的。
大值思路是对每个子树维护成一个大节点,存一些根啊,深度啊,到大节点根距离啊,节点编号范围啊之类的信息。
然后发现维护相对节点标号大小是个区间第k大,得对dfs序建一颗主席树
然后每次询问倍增跳一跳,讨论个几种情况之类的。
ps:别吐槽名字
Code:
#include <cstdio>
#include <cctype>
#include <algorithm>
#define int long long
const int N=1e5+10;
template <class T>
void read(T &x)
{
x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
int n,m,q;
namespace koito_yuu
{
int head[N],to[N<<1],Next[N<<1],cnt;
void add(int u,int v)
{
to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int f[18][N],dep[N],dfn[N],ha[N],siz[N],clock;
void dfs(int now)
{
ha[dfn[now]=++clock]=now;
dep[now]=dep[f[0][now]]+1;
siz[now]=1;
for(int i=1;f[i-1][now];i++) f[i][now]=f[i-1][f[i-1][now]];
for(int v,i=head[now];i;i=Next[i])
if((v=to[i])!=f[0][now])
f[0][v]=now,dfs(v),siz[now]+=siz[v];
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) std::swap(x,y);
for(int i=17;~i;i--)
if(dep[f[i][x]]>=dep[y])
x=f[i][x];
if(x==y) return x;
for(int i=17;~i;i--)
if(f[i][x]!=f[i][y])
x=f[i][x],y=f[i][y];
return f[0][x];
}
int getdis(int x,int y)
{
int lca=LCA(x,y);
return dep[x]+dep[y]-(dep[lca]<<1);
}
int ch[N*30][2],sum[N*30],root[N],tot;
#define ls ch[now][0]
#define rs ch[now][1]
#define ols ch[las][0]
#define ors ch[las][1]
void rebuild(int &now,int las,int l,int r,int p)
{
now=++tot;
if(l==r) {++sum[now];return;}
int mid=l+r>>1;
if(p<=mid) rebuild(ls,ols,l,mid,p),rs=ors;
else ls=ols,rebuild(rs,ors,mid+1,r,p);
sum[now]=sum[ls]+sum[rs];
}
int query(int now,int las,int l,int r,int k)
{
if(l==r) return l;
int mid=l+r>>1;
if(sum[ls]-sum[ols]>=k) return query(ls,ols,l,mid,k);
else return query(rs,ors,mid+1,r,k-(sum[ls]-sum[ols]));
}
void work()
{
for(int u,v,i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
dfs(1);
for(int i=1;i<=n;i++) rebuild(root[i],root[i-1],1,n,ha[i]);
}
}
using koito_yuu::siz;
using koito_yuu::dfn;
using koito_yuu::getdis;
namespace nanami_touko
{
int num,L[N],R[N],f[18][N],dis[N],root[N],dep[N],par[N];
void query(int x,int &whi,int &pos,int &rt)
{
whi=std::lower_bound(R+1,R+1+num,x)-R;
rt=root[whi];
pos=koito_yuu::query(koito_yuu::root[dfn[rt]+siz[rt]-1],koito_yuu::root[dfn[rt]-1],1,n,x-L[whi]+1);
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) std::swap(x,y);
for(int i=17;~i;i--)
if(dep[f[i][x]]>=dep[y])
x=f[i][x];
if(x==y) return x;
for(int i=17;~i;i--)
if(f[i][x]!=f[i][y])
x=f[i][x],y=f[i][y];
return f[0][x];
}
int clim(int x,int y)
{
for(int i=17;~i;i--)
if(dep[f[i][x]]>dep[y])
x=f[i][x];
return x;
}
void work()
{
root[++num]=1;
dep[num]=1,L[num]=1,R[num]=n;
for(int x,t,i=1;i<=m;i++)//子树x复制到t的儿子
{
read(x),read(t);
root[++num]=x;
L[num]=R[num-1]+1,R[num]=L[num]+siz[x]-1;
int pos,whi,rt;
query(t,whi,pos,rt);//t在哪个大节点,t在模板树的编号和大节点的根
par[num]=pos;
f[0][num]=whi,dis[num]=getdis(rt,pos)+1+dis[whi];
dep[num]=dep[whi]+1;
for(int j=1;f[j-1][num];j++) f[j][num]=f[j-1][f[j-1][num]];
}
}
}
using nanami_touko::root;
using nanami_touko::dis;
using nanami_touko::clim;
using nanami_touko::par;
namespace saeki_sayaka
{
int get(int a,int b,int c,int d,int ru,int rv)
{
return getdis(a,b)+dis[ru]-dis[rv]+getdis(c,d)+1;
}
void work()
{
for(int u,v,i=1;i<=q;i++)
{
read(u),read(v);
int whiu,rtu,whiv,rtv;
nanami_touko::query(u,whiu,u,rtu);
nanami_touko::query(v,whiv,v,rtv);
if(whiu==whiv)
{
printf("%lld\n",getdis(u,v));
continue;
}
if(nanami_touko::dep[whiu]<nanami_touko::dep[whiv])
std::swap(whiu,whiv),std::swap(u,v),std::swap(rtu,rtv);
int lca=nanami_touko::LCA(whiu,whiv);
if(lca==whiv)
{
int ancu=clim(whiu,lca);
printf("%lld\n",get(u,rtu,par[ancu],v,whiu,ancu));
continue;
}
int ancu=clim(whiu,lca),ancv=clim(whiv,lca);
int urt=par[ancu],vrt=par[ancv];
int p=koito_yuu::LCA(urt,vrt);
printf("%lld\n",get(u,rtu,urt,p,whiu,ancu)+get(v,rtv,vrt,p,whiv,ancv));
}
}
}
signed main()
{
read(n),read(m),read(q);
koito_yuu::work();
nanami_touko::work();
saeki_sayaka::work();
return 0;
}
2019.3.11
「HNOI2016」树 解题报告的更多相关文章
- 「HNOI2016」网络 解题报告
「HNOI2016」网络 我有一个绝妙的可持久化树套树思路,可惜的是,它的空间是\(n\log^2 n\)的... 注意到对一个询问,我们可以二分答案 然后统计经过这个点大于当前答案的路径条数,如果这 ...
- 「HNOI2016」序列 解题报告
「HNOI2016」序列 有一些高妙的做法,懒得看 考虑莫队,考虑莫队咋移动区间 然后你在区间内部找一个最小值的位置,假设现在从右边加 最小值左边区间显然可以\(O(1)\),最小值右边的区间是断掉的 ...
- 「HNOI2016」最小公倍数 解题报告
「HNOI2016」最小公倍数 考虑暴力,对每个询问,处理出\(\le a,\le b\)的与询问点在一起的联通块,然后判断是否是一个联通块,且联通块\(a,b\)最大值是否满足要求. 然后很显然需要 ...
- 「SCOI2016」美味 解题报告
「SCOI2016」美味 状态极差无比,一个锤子题目而已 考虑每次对\(b\)和\(d\)求\(c=d \ xor \ (a+b)\)的最大值,因为异或每一位是独立的,所以我们可以尝试按位贪心. 如果 ...
- 「SCOI2016」萌萌哒 解题报告
「SCOI2016」萌萌哒 这思路厉害啊.. 容易发现有个暴力是并查集 然后我想了半天线段树优化无果 然后正解是倍增优化并查集 有这个思路就简单了,就是开一个并查集代表每个开头\(i\)每个长\(2^ ...
- 「TJOI2015」概率论 解题报告
「TJOI2015」概率论 令\(f_i\)代表\(i\)个点树形态数量,\(g_i\)代表\(i\)个点叶子个数 然后列一个dp \[ f_i=\sum_{j=0}^{i-1} f_j f_{i-j ...
- 「SDOI2014」重建 解题报告
「SDOI2014」重建 题意 给一个图\(G\),两点\((u,v)\)有边的概率是\(p_{u,v}\),求有\(n-1\)条边通行且组成了一颗树的概率是多少. 抄了几个矩阵树定理有趣的感性说法 ...
- 「NOI2016」区间 解题报告
「NOI2016」区间 最近思维好僵硬啊... 一上来就觉得先把区间拆成两个端点进行差分,然后扫描位置序列,在每个位置维护答案,用数据结构维护当前位置的区间序列,但是不会维护. 于是想研究性质,想到为 ...
- 「ZJOI2019」语言 解题报告
「ZJOI2019」语言 3个\(\log\)做法比较简单,但是写起来还是有点麻烦的. 大概就是树剖把链划分为\(\log\)段,然后任意两段可以组成一个矩形,就是个矩形面积并,听说卡卡就过去了. 好 ...
随机推荐
- Servlet 使用ServletContext共享数据,读取web.xml配置
ServletContext对象 session和cookie,对于每一个请求用户来说,都是不同的,因为要保证隐私安全. 而有一些数据,可以让所有用户共享,此时就可以用ServletContext对象 ...
- PHP之CLI模式
转载: http://www.cnblogs.com/zcy_soft/archive/2011/12/10/2283437.html 所有的PHP发行版,不论是编译自源代码的版本还是预创建的版本,都 ...
- C#设计模式之1:策略模式
首先需要说明的是该系列的所有内容都是基于headfirst设计模式来描述的.因为我之前也看过不少关于设计模式的书,还是发现这本最好,因为这本书里面给出的例子是最贴切实际的.不说了,开始这个系列吧! 策 ...
- Linux上的一些基本常用命令
上传下载文件:// 首先安装lrzsz # yum -y install lrzsz // 上传文件,执行命令rz,会跳出文件选择窗口,选择好文件,点击确认即可.# rz // 下载文件,执行命令sz ...
- Day 4-2 time & datetime模块
time模块. import time time.time() #输出: 1523195163.140625 time.localtime() # 获取的是操作系统的时间,可以添加一个时间戳参数 # ...
- centOS 7下无法启动网络(service network start)错误解决办法
今天在centOS 7下更改完静态ip后发现network服务重启不了,翻遍了网络,尝试了各种方法,终于解决了. 现把各种解决方法归纳整理,希望能让后面的同学少走点歪路... 首先看问题:执行serv ...
- Prism框架研究(一)
从今天起开始写一个Prism框架的学习博客,今天是第一篇,所以从最基本的一些概念开始学习这个基于MVVM的框架的学习,首先看一下Prism代表什么,这里引用一下比较官方的英文解释来看一下:Prism ...
- 如何在集合中巧用Where来查找相关元素
在我们的项目中我们经常会查找一些集合中的重要元素,当然我们可以使用常规的foreach循环和if语句来查询,但是我们要学会使用System.Linq命名空间下面的静态类Enumerable下面的静态方 ...
- Java使用RabbitMQ之订阅分发(Topic)
使用RabbitMQ进行消息发布和订阅,生产者将消息发送给转发器(exchange),转发器根据路由键匹配已绑定的消息队列并转发消息,主题模式支持路由键的通配. 生产者代码: package org. ...
- Lodop打印旋转180度 倒着打
方法1:打印出来后,直接把纸张倒过来.如果本身是白纸,打印机出纸内容是倒着的,可以打出来后手动倒着把纸张正过来.如果本身不是白纸,需要打印的纸张上有背景,调整进纸方向.(如果是卷纸,卷纸背景是反的,查 ...