首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案。根据实现不同有下面几种方法。
三个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.lang.NoSuchMethodError: org.hibernate.Session.createQuery(Ljava/lang/String;)Lorg/hibernate/query/Query;]

    jar包冲突 maven导入的jar包和自己将lib目录的jar同时加入了项目里面了

  2. mycat启动报Unable to start JVM: No such file or directory (2)【转】

    mycat启动失败,查看日志 /mycat/logs/wrapper.log发现如下信息 1  STATUS | wrapper  | 2017/11/22 16:15:17 | --> Wra ...

  3. linux中截取字段与#、$区别

    1.Linux shell 截取字符变量的前8位 实现方法有如下几种: expr substr “$a” 1 8 echo $a|awk ‘{print substr(,1,8)}’ echo $a| ...

  4. 使用AWS Lambda,API Gateway和S3 Storage快速调整图片大小

    https://www.obytes.com/blog/2019/image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-stor ...

  5. 终于解决了python 3.x import cv2 “ImportError: DLL load failed: 找不到指定的模块” 及“pycharm关于cv2没有代码提示”的问题

    终于解决了python 3.x import cv2 “ImportError: DLL load failed: 找不到指定的模块” 及“pycharm关于cv2没有代码提示”的问题   参考 :h ...

  6. ISO/IEC 9899:2011 条款6.7.2——类型说明符

    6.7.2 类型说明符 语法 1.type-specifier: void char short int long float double signed unsigned _Bool _Comple ...

  7. RabbitMQ 入门教程(PHP版) 简单Demo

    RabbitMQ的关键字说明 (1)Broker:经纪人.提供一种传输服务,维护一条从生产者到消费者的传输线路,保证消息数据能按照指定的方式传输.粗略的可以将图中的RabbitMQ Server当作B ...

  8. Spring Cloud Eureka 服务发现 4.2

      在微服务架构中,服务发现可以说是最为核心和基础的模块,该模块主要用于实现各个微服务实例的自动化注册与发现.在Spring Cloud的子项目中,Spring Cloud Netflix提供了Eur ...

  9. 静态站点生成器-md-pelican

    推荐指数:

  10. LODOP统计table自动分页后的每页的某列合计值

    LODOP中超文本会根据打印项高度或超过纸张,自动分页.(相关博文:Lodop打印控件 超文本自动分页.LODOP中ADD_PRINT_TABLE.HTM.HTML表格自动分页测试.Lodop打印表格 ...