Luogu5327 ZJOI2019语言(树上差分+线段树合并)
暴力树剖做法显然,即使做到两个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语言(树上差分+线段树合并)的更多相关文章
- [Luogu5327][ZJOI2019]语言(树上差分+线段树合并)
首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路 ...
- [BZOJ3307] 雨天的尾巴(树上差分+线段树合并)
[BZOJ3307] 雨天的尾巴(树上差分+线段树合并) 题面 给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型. N, M≤100000 分析 ...
- [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类型的救济粮. 然后深绘里想知道,当所有的救济粮 ...
随机推荐
- rpm包和deb分别是什么?
一.RMP 是 LINUX 下的一种软件的可执行程序,你只要安装它就可以了.这种软件安装包通常是一个RPM包(Redhat Linux Packet Manager,就是Redhat的包管理器),后缀 ...
- DBeaver的时区问题
最近使用DBeaver作为连接MySQL的客户端,发现执行sql返回的datetime类型字段时间不对,比实际时间多了10多个小时, 无论是连接开发服务器或者连本机MySQL都有这个问题. 检查服务器 ...
- SpringBoot视图层技术
一.SpringBoot整合jsp 在maven的dependencies的依赖中除了springBoot启动器还要添加对jstl和jsp的依赖. <dependencies> <d ...
- Vue 缓存当前页面keep-alive
需求: 产品经理在列表页(几千个数据,n个page)点击某一项进去到详情页后,再返回到列表页发现页面回到了第一页,找不到之前的查看的是哪一条了,为了方便咋公司产品经理,返回列表页时需要记住之前的pag ...
- PHP根据传入的经纬度,和距离范围,返回所有在距离范围内的经纬度的取值范围
/** * 根据传入的经纬度,和距离范围,返回所有在距离范围内的经纬度的取值范围 * @param float $lng 经度 * @param float $lat 纬度 * @param floa ...
- Paper reading: High-Fidelity Pose and Expression Normalization for Face Recognition in the Wild(HPEN)
1. Introduction 人脸识别受到各种因素影响,其中最重要的两个影响是 pose 和 expression, 这两个因素会对 intra-person 变化产生极大的影响, 有时候甚至会超过 ...
- 第十一章 缓存机制——《跟我学Shiro》
转发地址:https://www.iteye.com/blog/jinnianshilongnian-2029217 跟我学Shiro 目录贴:跟我学Shiro目录贴 Shiro提供了类似于Spri ...
- 第四章 INI配置——《跟我学Shiro》
转发地址:https://www.iteye.com/blog/jinnianshilongnian-2020820 第四章 INI配置——<跟我学Shiro> 博客分类: 跟我学Shir ...
- C入门笔记
教程总体概括:Mac OS X系统简介:C语言:OC语言:Foundation:iOS开发:项目实战. 3.第一个c语言程序#include <stdio.h>//预处理指令:在编译之前执 ...
- linux查看物理cpu的核数,个数,逻辑cpu的个数
# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 # 查看物理CPU个数 cat /proc/cpuinfo| ...