首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案。根据实现不同有下面几种方法。
三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路径上所有点的S中都加入这条路径。树剖之后,相当于对log个区间中的点都加入log个区间。具体实现有树剖后线段树维护虚树、矩形扫描线、线段树+set存区间等多种方法,这里不再多说。
两个log:先树剖,然后对每个点开一棵线段树存储它的S,由于题中没有修改,所以可以树上差分+线段树合并,这样可以将方法一中“需要修改的区间数”的log去掉了。
一个log:发现就是对每个点求所有经过它的路径的端点的斯坦纳树(这里一个点集的斯坦纳树是指原树上最小的点集,满足包含这个点集且连通)。考虑如何暴力求一个点集的斯坦纳树,那显然就是将它们按DFS序排序后,所有点深度之和减去每对相邻点LCA的深度和。为了方便我们将点集中强制加入根,最后求出结果后再减去所有点LCA的深度的两倍。以DFS序为下标建线段树,每个点维护它所代表的DFS区间中,所有在点集中的点(加上根)构成的斯坦纳树的大小。两个区间的合并就是两边的斯坦纳树大小之和,减去左边区间里在点集中的DFS序最大的点与右边区间里在点集中的DFS序最小的点的LCA的深度,于是再维护区间里在点集中的DFS序最大/小的点分别是谁即可。同样使用树上差分+线段树合并,就可以将方法一中“每个修改区间中要加入的区间数”的log去掉了。

(参考https://www.luogu.org/blog/Sooke/solution-p5327

 #include<cstdio>
#include<vector>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
typedef long long ll;
using namespace std; const int N=,M=,K=;
int n,m,x,y,tim,cnt,nd,d[N],lg[N],rt[N],fa[N],dfn[N],st[N][];
int v[M],ls[M],rs[M],s[M],t[M],c[M],h[N],to[N],nxt[N];
ll ans;
vector<int>del[N]; void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int x){
d[x]=d[fa[x]]+; st[++tim][]=x; dfn[x]=tim;
For(i,x) if ((k=to[i])!=fa[x]) fa[k]=x,dfs(k),st[++tim][]=x;
} void init(){
rep(j,,lg[tim]) rep(i,,tim-(<<j)+){
int x=st[i][j-],y=st[i+(<<(j-))][j-];
st[i][j]=d[x]<d[y] ? x : y;
}
} int lca(int x,int y){
if (!x || !y) return ;
x=dfn[x]; y=dfn[y];
if (x>y) swap(x,y);
int t=lg[y-x+]; x=st[x][t]; y=st[y-(<<t)+][t];
return d[x]<d[y] ? x : y;
} void upd(int x){
v[x]=v[ls[x]]+v[rs[x]]-d[lca(t[ls[x]],s[rs[x]])];
s[x]=s[ls[x]] ? s[ls[x]] : s[rs[x]];
t[x]=t[rs[x]] ? t[rs[x]] : t[ls[x]];
} void mdf(int &x,int L,int R,int p,int k){
if (!x) x=++nd;
if (L==R){ c[x]+=k; v[x]=(c[x]?d[p]:); s[x]=t[x]=(c[x]?p:); return; }
int mid=(L+R)>>;
if (dfn[p]<=mid) mdf(ls[x],L,mid,p,k); else mdf(rs[x],mid+,R,p,k);
upd(x);
} int merge(int x,int y,int L,int R){
if (!x || !y) return x|y;
if (L==R){ c[x]+=c[y]; v[x]|=v[y]; s[x]|=s[y]; t[x]|=t[y]; return x; }
int mid=(L+R)>>; ls[x]=merge(ls[x],ls[y],L,mid); rs[x]=merge(rs[x],rs[y],mid+,R);
upd(x); return x;
} void solve(int x){
For(i,x) if ((k=to[i])!=fa[x]) solve(k);
int ed=del[x].size()-;
rep(i,,ed) mdf(rt[x],,tim,del[x][i],-);
ans+=v[rt[x]]-d[lca(s[rt[x]],t[rt[x]])]; rt[fa[x]]=merge(rt[fa[x]],rt[x],,tim);
} int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n<<) lg[i]=lg[i>>]+;
rep(i,,n) scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(); init();
rep(i,,m){
scanf("%d%d",&x,&y); int l=lca(x,y);
mdf(rt[x],,tim,x,); mdf(rt[x],,tim,y,);
mdf(rt[y],,tim,x,); mdf(rt[y],,tim,y,);
del[l].push_back(x); del[l].push_back(y);
del[fa[l]].push_back(x); del[fa[l]].push_back(y);
}
solve(); printf("%lld\n",ans/);
return ;
}

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

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

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

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

    暴力树剖做法显然,即使做到两个log也不那么优美. 考虑避免树剖做到一个log.那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并 ...

  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. Java-Maven(十):Maven 项目常用plugins

    本文主要总结最近一段时间使用maven时,遇到需要maven plugins的一些简单总结. 1)在Build下重新指定最终打包报名 <build> <!--最终打包的包名,如果这里 ...

  2. 【转】自动化框架中引入ExtentReport美化报告

    本文链接:https://blog.csdn.net/qq_30353203/article/details/82023922一.先引入三个依赖包 <dependency> <gro ...

  3. 服务端高并发分布式架构 ESB 企业服务总线

    服务端高并发分布式架构演进之路 - 个人文章 - SegmentFault 思否 https://segmentfault.com/a/1190000018626163 ESB 企业服务总线讲解 ht ...

  4. 如何贡献补丁到uboot社区?

    答: 首次贡献分为两步: 1. 首先需要订阅一下,地址在此https://lists.denx.de/listinfo/u-boot,使邮箱地址对应有一个成员名称,才能向uboot社区发送补丁,否则会 ...

  5. Office Tool Plus

    https://otp.landian.vip/zh-cn/index.html 本软件支持Microsoft Office办公软件最新版本高速下载.卸载.修复.定制化安装.静默安装等诸多功能. 同时 ...

  6. Django架站的16堂課

    Django架站的16堂課-活用Django+Web+Framework快速构建移动网站 目录 第1堂 网站开发环境的建立 1 1.1 网站的基础知识 1 1.1.1 网站的运行流程 1 1.1.2 ...

  7. LeetCode_299. Bulls and Cows

    299. Bulls and Cows Easy You are playing the following Bulls and Cows game with your friend: You wri ...

  8. ASP.NET全球化与本地化 c#多国语言的支持 (项目支持多国语言的开发)

    ASP.NET 2.0及以上的开发平台,为全球化本地化应用程序提供了工具,而且实现起来非常简单.以下内容是使用c#,按照帮助一步步做的,将为初学者提供详细的实现步骤. 一 几个必要概念 (一) 支持全 ...

  9. gluster设置日志级别

    glusterd --log-level WARNING #将日志级别设定为warning gluster --log-level=ERROR volume status #查看日志级别的状态 glu ...

  10. go 的基本数据类型和结构

    // 变量命名方式当用两个或两个以上的单词命名变量时,可以将除第一个单词以外的所有单词的首字母大写package 命名方式全部小写不加 _ 布尔类型:bool整型:int8.byte.int16.in ...