[Luogu5327][ZJOI2019]语言(树上差分+线段树合并)
首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案。根据实现不同有下面几种方法。
三个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]语言(树上差分+线段树合并)的更多相关文章
- [BZOJ3307] 雨天的尾巴(树上差分+线段树合并)
[BZOJ3307] 雨天的尾巴(树上差分+线段树合并) 题面 给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型. N, M≤100000 分析 ...
- Luogu5327 ZJOI2019语言(树上差分+线段树合并)
暴力树剖做法显然,即使做到两个log也不那么优美. 考虑避免树剖做到一个log.那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并 ...
- [ZJOI2019]语言——树剖+树上差分+线段树合并
原题链接戳这儿 SOLUTION 考虑一种非常\(naive\)的统计方法,就是对于每一个点\(u\),我们维护它能到达的点集\(S_u\),最后答案就是\(\frac{\sum\limits_{i= ...
- 2018.08.28 洛谷P4556 [Vani有约会]雨天的尾巴(树上差分+线段树合并)
传送门 要求维护每个点上出现次数最多的颜色. 对于每次修改,我们用树上差分的思想,然后线段树合并统计答案就行了. 注意颜色很大需要离散化. 代码: #include<bits/stdc++.h& ...
- bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】
这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...
- BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)
题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...
- P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)
显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...
- [NOIP2016]天天爱跑步(树上差分+线段树合并)
将每个人跑步的路径拆分成x->lca,lca->y两条路径分别考虑: 对于在点i的观察点,这个人(s->t)能被观察到的充要条件为: 1.直向上的路径:w[i]=dep[s]-dep ...
- [Vani有约会]雨天的尾巴(树上差分+线段树合并)
首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮. 然后深绘里想知道,当所有的救济粮 ...
随机推荐
- 模型稳定性指标—PSI
由于模型是以特定时期的样本所开发的,此模型是否适用于开发样本之外的族群,必须经过稳定性测试才能得知.稳定度指标(population stability index ,PSI)可衡量测试样本及模型开发 ...
- eclipse如何配置tomcat
依次点击Window->Show View->Other 找到Server,点击OK,看到控制台旁边已有Server了. 点击server下面的链接,依次选择Apache,根据自己 ...
- cv2.imread()
cv2.imread() 使用opencv和caffe的伙伴们,可能会有一个疑问,那就是对于同时读取图片的cv2.imread()和caffe.io.loadimage两个函数,有什么差别? 1.cv ...
- 小D课堂 - 零基础入门SpringBoot2.X到实战_第5节 SpringBoot部署war项目到tomcat9和启动原理讲解_22、SpringBoot启动方式和部署war项目到tomcat9
笔记 1.SpringBoot启动方式讲解和部署war项目到tomcat9 简介:SpringBoot常见启动方式讲解和部署war项目Tomcat 1.ide启动 2.jar包方式启动 ...
- ISO/IEC 9899:2011 条款6.3——转换
6.3 转换 1.有些操作符将操作数的值自动地从一种类型转换为另一种.本子条款指定了从这么一个隐式转换所要求的结果,以及从一个投射操作(一个显式转换)所要求的结果.在6.3.1.8中所列出的信息概括了 ...
- java获取全部子类或接口的全部实现
在JAVA中,获取一个类的全部父类是比较简单的,只需要通过反射(Class的getSuperclass()方法)即可.然而,如果想获得一个类的所有子类,或者获得实现某一个接口的所有实现类,相对比较麻烦 ...
- Spring cloud微服务安全实战-5-11实现基于token的SSO(1)
基于服务器session的SSO的方案,用户的登陆状态都是存在服务器端的 基于浏览器cookie的token实现一个sso,在原来的基础上做进一步的改造.让他不依赖session.而是完全由cooki ...
- Spring cloud微服务安全实战-3-1 API安全 常见的安全机制
不考虑微服务这种复杂的环境下,只是写一个简单的api的时候,如何来保证api的安全. 什么是API
- Hadoop记录-部署hadoop环境shell实现
#!/bin/bash menu() { echo "---欢迎使用hadoop部署管理程序---" echo "# 1.初始化Linux环境" echo &q ...
- 基于jsplumb插件制作可拖拽、保存流程图、重绘保存后的流程图总结
1.重点参考博文 https://blog.csdn.net/j_bean/article/details/78092647 2.关键点总结 1)实现可视区域图形画满后,拖动整个画布的效果 a.最好不 ...