[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类型的救济粮. 然后深绘里想知道,当所有的救济粮 ...
随机推荐
- phpstorm 使用xdebug断点
1.下载对应版本 xdebug 下载地址:https://download.csdn.net/download/q2104574/11185239 比如你用的php7.0.12以上的版本,xdebug ...
- HmacSHA256摘要算法
mac(Message Authentication Code,消息认证码算法)是含有密钥散列函数算法,兼容了MD和SHA算法的特性,并在此基础上加上了密钥. 甲乙双方进行数据交换可以采取如下流程完成 ...
- 【vue】常用操作
一.Vue中import from的来源:省略后缀与加载文件夹 https://blog.csdn.net/fyyyr/article/details/83657828 二.Vue安装依赖 #安装依赖 ...
- python清理docker-harbor的多余镜像
# coding: utf-8 from operator import itemgetter from urllib import parse import requests import date ...
- ZingChart 隐藏数据点
正常情况下 zingChart 的数据点会显示到图表中,但是如果数据点很多的情况下,可能会让你无法准确的预测趋势,而且也不美观 在 js 配置中添加最多允许显示的数据点,超过这个值将不显示数据点 效果 ...
- 微信小程序与Html交互
微信小程序与H5页面交互 https://www.jianshu.com/p/22e951d83841
- Jmeter之__CSVRead随机读取变量
背景:当你需要从一些数据中随机的取值用的话可以用这个函数来实现__CSVRead __CSVRead函数用于对脚本进行参数化,当脚本中不同变量需要不同参数值时,可以考虑__CSVRead函数. _CS ...
- ES6深入浅出-4 迭代器与生成器-2.Symbol 和迭代器
symbol https://zhuanlan.zhihu.com/p/22652486 Es5中的数据类型,所有的复杂类型都是对象类型. ES6里面增加了symbol类型,用处不多. https:/ ...
- PAT 甲级 1146 Topological Order (25 分)(拓扑较简单,保存入度数和出度的节点即可)
1146 Topological Order (25 分) This is a problem given in the Graduate Entrance Exam in 2018: Which ...
- C# 实现生产者消费者队列 (转)
按语:按照下面文档,测试成功: https://www.cnblogs.com/samgk/p/4772806.html 开发过程中经常会碰到这样的场景:需要从一个地方获取一些数据,然后处理数据并将其 ...