「ZJOI2019」语言
传送门
Description
给定一棵\(n\)个点的树和\(m\)条链,两个点可以联会当且仅当它们同在某一条链上,求可以联会的点的方案数
\(n,m\leq10^5\)
Solution
考虑计算每个点的贡献,然后将贡献和除以\(2\)
相当于求出对于每个点,经过它的所有链的端点的虚树的大小(显然这些链的并就是一个树)
每个虚树都先加上了根节点,可以保证以下求虚树大小的做法合法:
注意:这里的根节点是\(1\)号节点,但是我们并不看作\(1\)号节点一定在这个虚树内——它更像是一个等价于\(1\)号点的虚拟节点
先对所有端点按照\(dfs\)序进行排序,这样根肯定在第一个
然后在按顺序加入每个点的过程中
加上这个点连向当前虚树的最短链的长度,也就是\(dep_{a[i]}-dep_{lca(a[i],a[i-1])}\)
\(last\)指的是上一个加入的点
对于最小的那个节点,它的贡献就是\(dep[a[1]]\)
我们考虑线段树的分治的过程,假设我们先分别求出了\(dfs\)序在\([l,mid]\)和\([mid+1,r]\)中端点的虚树信息
接下来要将其合并,这时,其实只需要合并左区间中最大的和右区间中最小的即可,这时我们是不算根节点的
合并之后,发现多算了一些点,具体来说,是重复计算了从根到\(LCA\)的一段路径,将其减去
最后,求得虚树大小后,还要减去从根到\(LCA\)的长度,在合并的同时维护一下集合的\(lca\)即可
在本题中,求\(lca\)采用\(RMQ\)方法
树上差分,在端点和端点的\(lca\)的父亲处进行加点和删点操作
相当于对线段树进行单点修改
从孩子那里继承信息需要用到线段树合并
复杂度是\(O(n\log n)\)
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define reg register
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int MN=1e5+5,MM=4e6+5;
int n,m;ll ans;
struct ed{int to,nex;}e[MN<<1];int hr[MN],en;
inline void ins(int x,int y)
{
e[++en]=(ed){y,hr[x]};hr[x]=en;
e[++en]=(ed){x,hr[y]};hr[y]=en;
}
/*------------求lca------------*/
int st[MN<<1][20],eu[MN],id[MN],dfn[MN],pin,dep[MN],fa[MN],dind,lg[MN<<1];
void dfs(int x,int f)
{
id[dfn[x]=++pin]=x;st[dind][0]=x;
eu[x]=++dind;dep[x]=dep[fa[x]=f]+1;
for(int i=hr[x];i;i=e[i].nex)if(e[i].to^f)
st[dind][0]=x,++dind,dfs(e[i].to,x);
}
inline void init()
{
dfs(1,0);register int i,j;
for(i=2;i<=dind;++i)lg[i]=lg[i>>1]+1;
for(j=1;j<20;++j)for(i=1;i<=dind;++i)
{
if(i+(1<<j)>dind) break;
st[i][j]=dep[st[i][j-1]]<dep[st[i+(1<<j-1)][j-1]]?st[i][j-1]:st[i+(1<<j-1)][j-1];
}
}
int LCA(int x,int y)
{
if(!x||!y) return x|y;
if(x==y) return x;
x=eu[x];y=eu[y];if(x>y)swap(x,y);
int b=lg[y-x];
if(dep[st[x][b]]<dep[st[y-(1<<b)][b]])
return st[x][b];
return st[y-(1<<b)][b];
}
/*------------线段树合并------------*/
struct xxx{
int x,y,z;
xxx(int x,int y,int z):x(x),y(y),z(z){}
};
std::vector<xxx> opt[MN];
int ml[MM],mr[MM],ls[MM],rs[MM],v[MM],sum[MM],lca[MM],rt[MN],tot;
void up(int x)
{
ml[x]=ml[ls[x]]?ml[ls[x]]:ml[rs[x]];
mr[x]=mr[rs[x]]?mr[rs[x]]:mr[ls[x]];
v[x]=v[ls[x]]+v[rs[x]];
if(!v[ls[x]]||!v[rs[x]]) sum[x]=sum[ls[x]]+sum[rs[x]];
else sum[x]=sum[ls[x]]+sum[rs[x]]-dep[LCA(id[mr[ls[x]]],id[ml[rs[x]]])];
if(lca[ls[x]]&&lca[rs[x]]) lca[x]=LCA(lca[ls[x]],lca[rs[x]]);
else lca[x]=lca[ls[x]]|lca[rs[x]];
}
int Merge(int x,int y,int l,int r)
{
if(!x||!y) return x|y;
if(l==r)
{
v[x]+=v[y];
if(v[x]) ml[x]=mr[x]=l,lca[x]=id[l],sum[x]=dep[id[l]];
else ml[x]=mr[x]=lca[x]=sum[x]=0;
return x;
}
int mid=(l+r)>>1;
ls[x]=Merge(ls[x],ls[y],l,mid);
rs[x]=Merge(rs[x],rs[y],mid+1,r);
up(x);return x;
}
void Modify(int &x,int l,int r,int a,int b)
{
if(!x) x=++tot;
if(l==r)
{
v[x]+=b;
if(v[x]) ml[x]=mr[x]=l,lca[x]=id[l],sum[x]=dep[id[l]];
else ml[x]=mr[x]=lca[x]=sum[x]=0;
return;
}
int mid=(l+r)>>1;
if(a<=mid) Modify(ls[x],l,mid,a,b);
else Modify(rs[x],mid+1,r,a,b);
up(x);
}
void Solve(int x)
{
register int i;
for(i=hr[x];i;i=e[i].nex)if(e[i].to^fa[x])
Solve(e[i].to),rt[x]=Merge(rt[x],rt[e[i].to],1,n);
for(i=opt[x].size()-1;~i;--i)
Modify(rt[x],1,n,opt[x][i].x,opt[x][i].z),
Modify(rt[x],1,n,opt[x][i].y,opt[x][i].z);
ans+=sum[rt[x]]-dep[lca[rt[x]]];
}
int main()
{
n=read();m=read();
register int i,x,y,z;
for(i=1;i<n;++i) x=read(),ins(x,read());
init();
for(i=1;i<=m;++i)
{
x=read(),y=read();z=LCA(x,y);
opt[x].push_back(xxx(dfn[x],dfn[y],1));
opt[y].push_back(xxx(dfn[x],dfn[y],1));
opt[z].push_back(xxx(dfn[x],dfn[y],-1));
opt[fa[z]].push_back(xxx(dfn[x],dfn[y],-1));
}
ans=0;Solve(1);
return 0*printf("%lld\n",ans>>1);
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!
「ZJOI2019」语言的更多相关文章
- 【LOJ】#3046. 「ZJOI2019」语言
LOJ#3046. 「ZJOI2019」语言 先orz zsy吧 有一个\(n\log^3n\)的做法是把树链剖分后,形成logn个区间,这些区间两两搭配可以获得一个矩形,求矩形面积并 然后就是对于一 ...
- 「ZJOI2019」语言 解题报告
「ZJOI2019」语言 3个\(\log\)做法比较简单,但是写起来还是有点麻烦的. 大概就是树剖把链划分为\(\log\)段,然后任意两段可以组成一个矩形,就是个矩形面积并,听说卡卡就过去了. 好 ...
- @loj - 3046@「ZJOI2019」语言
目录 @description@ @solution@ @accepted code@ @details@ @description@ 九条可怜是一个喜欢规律的女孩子.按照规律,第二题应该是一道和数据 ...
- 【线段树 树链剖分 差分 经典技巧】loj#3046. 「ZJOI2019」语言【未完】
还是来致敬一下那过往吧 题目分析 先丢代码 #include<bits/stdc++.h> ; ; ; struct node { int top,son,fa,tot; }a[maxn] ...
- bzoj5518 & loj3046 「ZJOI2019」语言 线段树合并+树链的并
题目传送门 https://loj.ac/problem/3046 题解 首先问题就是问有多少条路径是给定的几条路径中的一条的一个子段. 先考虑链的做法. 枚举右端点 \(i\),那么求出 \(j\) ...
- 「ZJOI2019」&「十二省联考 2019」题解索引
「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...
- LOJ3044. 「ZJOI2019」Minimax 搜索
LOJ3044. 「ZJOI2019」Minimax 搜索 https://loj.ac/problem/3044 分析: 假设\(w(1)=W\),那么使得这个值变化只会有两三种可能,比\(W\)小 ...
- Loj #3044. 「ZJOI2019」Minimax 搜索
Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ...
- Loj #3045. 「ZJOI2019」开关
Loj #3045. 「ZJOI2019」开关 题目描述 九条可怜是一个贪玩的女孩子. 这天,她和她的好朋友法海哥哥去玩密室逃脱.在他们面前的是 \(n\) 个开关,开始每个开关都是关闭的状态.要通过 ...
随机推荐
- K8S 中的容器编排和应用编排
众所周知,Kubernetes 是一个容器编排平台,它有非常丰富的原始的 API 来支持容器编排,但是对于用户来说更加关心的是一个应用的编排,包含多容器和服务的组合,管理它们之间的依赖关系,以及如何管 ...
- Excel 2010同时打开2个或多个独立窗口
亲测有效 参考下面的网址 https://jingyan.baidu.com/article/86fae346acca7d3c49121ad4.html 1. 在win+r 输入框里面输入“rege ...
- permission 权限清单
<uses-permission android:name="android.permission.READ_CALENDAR" /> <uses-permiss ...
- 微服务架构ServiceMesh
公司用的架构,在此找了资料作为记录复看所用: 什么是Service Mesh? Service Mesh的概念最早是由Buoyant公司的CEO William Morgan在一篇文章里提出,他给出的 ...
- 【开发笔记】- 永远不要在MySQL中使用UTF-8
原文地址:https://mp.weixin.qq.com/s/I3Tkvn8vSyC5lEpD9HzwiA 最近我遇到了一个bug,我试着通过Rails在以“utf8”编码的MariaDB中保存一个 ...
- unity shader入门(四):高光
高光反射计算公式(phong模型)Cspecular=(Clight*Mspecular)max(0,v*r)mgloss mgloss为材质的官泽度,也成反射度,控制高光区域亮点有多大 Mspecu ...
- nginx反向代理前后端分离项目(后端多台)
目前软件架构都比较流行前后端分离,前后端的分离也实现了前后端架构的分离,带来的好处 —— 整个项目的开发权重往前移,实现真正的前后端解耦,动态资源和静态资源分离,提高了性能和扩展性. 通常Spring ...
- node.js 学习一
Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. 与PHP 相似 都是单进程. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调 ...
- 解决iOS微信H5支付跳转微信后不返回App问题(Swift-WKWebview)(转)
问题分析 正常的H5支付流程如下 按照上面的支付流程会出现 App -> 微信 -> 支付 -> 点击 完成 -> safari访问redirect_url设置的URL,这种流 ...
- 【Docker】docker安装Jenkins
一.下载镜像 docker pull jenkinsci/jenkins 二.运行Jenkins容器 docker run --name myjenkins -d -p 8580:8080 -p 50 ...