暴力树剖做法显然,即使做到两个log也不那么优美。

  考虑避免树剖做到一个log。那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并支持在一个log内插入删除合并。

  考虑怎么求树上一些点所构成的斯坦纳树大小。由虚树的构造过程容易联想到,这就是按dfs序排序后这些点的深度之和-相邻点的lca的深度之和(首尾视作相邻),也就相当于按dfs序遍历所有要经过的点并回到原点的路径长度/2。

  这个东西显然(应该)可以set启发式合并维护,但同样就变成了两个log。可以改为线段树合并,线段树上每个节点维护该dfs序区间内dfs序最小和最大的被选中节点,合并时减去跨过两区间的一对相邻点的lca的深度即可。这需要计算O(nlogn)次lca,使用欧拉序rmq做到O(1)lca查询就能以总复杂度O(nlogn)完成。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,m,p[N],dfn[N],id[N],fa[N],deep[N],cnt,t;
struct data{int to,nxt;
}edge[N<<1];
vector<int> ins[N],del[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k)
{
dfn[k]=++cnt;id[cnt]=k;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k])
{
fa[edge[i].to]=k;
deep[edge[i].to]=deep[k]+1;
dfs(edge[i].to);
}
}
namespace euler_tour
{
int dfn[N],id[N<<1],LG2[N<<1],f[N<<1][19],cnt;
void dfs(int k)
{
dfn[k]=++cnt;id[cnt]=k;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k])
{
dfs(edge[i].to);
id[++cnt]=k;
}
}
void build()
{
dfs(1);
for (int i=1;i<=cnt;i++) f[i][0]=id[i];
for (int j=1;j<19;j++)
for (int i=1;i<=cnt;i++)
if (deep[f[i][j-1]]<deep[f[min(cnt,i+(1<<j-1))][j-1]]) f[i][j]=f[i][j-1];
else f[i][j]=f[min(cnt,i+(1<<j-1))][j-1];
for (int i=2;i<=cnt;i++)
{
LG2[i]=LG2[i-1];
if ((2<<LG2[i])<=i) LG2[i]++;
}
}
int lca(int x,int y)
{
if (!x||!y) return 0;
x=dfn[x],y=dfn[y];
if (x>y) swap(x,y);
if (deep[f[x][LG2[y-x+1]]]<deep[f[y-(1<<LG2[y-x+1])+1][LG2[y-x+1]]]) return f[x][LG2[y-x+1]];
else return f[y-(1<<LG2[y-x+1])+1][LG2[y-x+1]];
}
}
using euler_tour::lca;
ll ans;
int root[N];
struct data2{int l,r,cnt,lnode,rnode,ans;
}tree[N<<6];
void up(int k)
{
tree[k].lnode=tree[tree[k].l].lnode;if (!tree[k].lnode) tree[k].lnode=tree[tree[k].r].lnode;
tree[k].rnode=tree[tree[k].r].rnode;if (!tree[k].rnode) tree[k].rnode=tree[tree[k].l].rnode;
tree[k].ans=tree[tree[k].l].ans+tree[tree[k].r].ans-deep[lca(tree[tree[k].l].rnode,tree[tree[k].r].lnode)];
}
int merge(int x,int y,int l,int r)
{
if (!x||!y) return x|y;
if (l==r)
{
tree[x].cnt+=tree[y].cnt;
if (tree[x].cnt==0) tree[x].lnode=tree[x].rnode=tree[x].ans=0;
else tree[x].lnode=tree[x].rnode=id[l],tree[x].ans=deep[id[l]];
return x;
}
int mid=l+r>>1;
tree[x].l=merge(tree[x].l,tree[y].l,l,mid);
tree[x].r=merge(tree[x].r,tree[y].r,mid+1,r);
up(x);
return x;
}
void modify(int &k,int l,int r,int x,int op)
{
if (!k) k=++cnt;
if (l==r)
{
tree[k].cnt+=op;
if (tree[k].cnt==0) tree[k].lnode=tree[k].rnode=tree[k].ans=0;
else tree[k].lnode=tree[k].rnode=id[l],tree[k].ans=deep[id[l]];
return;
}
int mid=l+r>>1;
if (x<=mid) modify(tree[k].l,l,mid,x,op);
else modify(tree[k].r,mid+1,r,x,op);
up(k);
}
void solve(int k)
{
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k])
{
solve(edge[i].to);
root[k]=merge(root[k],root[edge[i].to],1,n);
}
for (int i:ins[k]) modify(root[k],1,n,dfn[i],1);
for (int i:del[k]) modify(root[k],1,n,dfn[i],-1);
ans+=tree[root[k]].ans-deep[lca(tree[root[k]].lnode,tree[root[k]].rnode)];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();
for (int i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
dfs(1);
euler_tour::build();
for (int i=1;i<=m;i++)
{
int x=read(),y=read(),z=fa[lca(x,y)];
ins[x].push_back(x);ins[x].push_back(y);
ins[y].push_back(x);ins[y].push_back(y);
del[z].push_back(x);del[z].push_back(y);
del[z].push_back(x);del[z].push_back(y);
}
cnt=0;
solve(1);
cout<<ans/2;
return 0;
}

  

Luogu5327 ZJOI2019语言(树上差分+线段树合并)的更多相关文章

  1. [Luogu5327][ZJOI2019]语言(树上差分+线段树合并)

    首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路 ...

  2. [BZOJ3307] 雨天的尾巴(树上差分+线段树合并)

    [BZOJ3307] 雨天的尾巴(树上差分+线段树合并) 题面 给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型. N, M≤100000 分析 ...

  3. [ZJOI2019]语言——树剖+树上差分+线段树合并

    原题链接戳这儿 SOLUTION 考虑一种非常\(naive\)的统计方法,就是对于每一个点\(u\),我们维护它能到达的点集\(S_u\),最后答案就是\(\frac{\sum\limits_{i= ...

  4. 2018.08.28 洛谷P4556 [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    传送门 要求维护每个点上出现次数最多的颜色. 对于每次修改,我们用树上差分的思想,然后线段树合并统计答案就行了. 注意颜色很大需要离散化. 代码: #include<bits/stdc++.h& ...

  5. bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】

    这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...

  6. BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)

    题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...

  7. P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)

    显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...

  8. [NOIP2016]天天爱跑步(树上差分+线段树合并)

    将每个人跑步的路径拆分成x->lca,lca->y两条路径分别考虑: 对于在点i的观察点,这个人(s->t)能被观察到的充要条件为: 1.直向上的路径:w[i]=dep[s]-dep ...

  9. [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮. 然后深绘里想知道,当所有的救济粮 ...

随机推荐

  1. LeetCode 第 151 场周赛

    一.查询无效交易(LeetCode-1169) 1.1 题目描述 1.2 解题思路 根据,它和另一个城市中同名的另一笔交易相隔不超过 60 分钟(包含 60 分钟整) 得出 城市A和其他城市任何一笔交 ...

  2. SQL-W3School-函数:SQL FORMAT() 函数

    ylbtech-SQL-W3School-函数:SQL FORMAT() 函数 1.返回顶部 1. FORMAT() 函数 FORMAT 函数用于对字段的显示进行格式化. SQL FORMAT() 语 ...

  3. flutter的生命周期

    大致可以看成三个阶段 初始化(插入渲染树) 状态改变(在渲染树中存在) 销毁(从渲染树种移除) initState 当插入渲染树的时候调用,这个函数在生命周期中只调用一次.这里可以做一些初始化工作,比 ...

  4. 阶段5 3.微服务项目【学成在线】_day07 课程管理实战_05-课程修改实战分析

    3 课程信息修改 3.1 需求分析 课程添加成功进入课程管理页面,通过课程管理页面修改课程的基本信息.编辑课程图片.编辑课程营销信息等. 本小节实现修改课程. 3.2 课程管理页面说明 3.2.1 页 ...

  5. CDS视图篇 1

    CDS视图概览 CDS是Core Data Services的简称,是HANA数据库向上层ABAP应用层提供数据的一种高效的方式,CDS模型是基于数据库data definition language ...

  6. 利用python求解物理学中的双弹簧质能系统详解

    利用python求解物理学中的双弹簧质能系统详解 本文主要给大家介绍了关于利用python求解物理学中双弹簧质能系统的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 物理的 ...

  7. 小程序下载canvas生成图片

    save_share_img:function(img){ var that = this; let { result } = that.data; getData.getData( "sa ...

  8. AssetsLibrary 实现访问相册,选取多张照片显示

    #import "ViewController.h" #import <AssetsLibrary/AssetsLibrary.h> /** * ALAssetsLib ...

  9. 记一次线上由nginx upstream keepalive与http协议"协作"引起的接口报错率飙高事件

    年前接到个任务,说要解决线上一些手机客户端接口报错率很高的问题.拿到了监控邮件,粗略一看,各种50%+的错误率,简直触目惊心.这种疑难杂症解决起来还是挺好玩的,于是撸起袖子action. 最终的结果虽 ...

  10. 2019-11-8 Linux作业 李宗盛

    linux系统的特点:完全免费,高效,安全稳定.支持多种硬件平台.有好的用户界面.强大的网络功能.支持多任务多用户. linux一般有三个主要部分:内核.命令解释层.实用工具. 那合适系统的心脏,是运 ...