@loj - 3046@「ZJOI2019」语言
@description@
九条可怜是一个喜欢规律的女孩子。按照规律,第二题应该是一道和数据结构有关的题。
在一个遥远的国度,有 n 个城市。城市之间有 n - 1 条双向道路,这些道路保证了任何两个城市之间都能直接或者间接地到达。
在上古时代,这 n 个城市之间处于战争状态。在高度闭塞的环境中,每个城市都发展出了自己的语言。而在王国统一之后,语言不通给王国的发展带来了极大的阻碍。为了改善这种情况,国王下令设计了 m 种通用语,并进行了 m 次语言统一工作。在第 i 次统一工作中,一名大臣从城市 si 出发,沿着最短的路径走到了 ti,教会了沿途所有城市(包括 si, ti)使用第 i 个通用语。
一旦有了共通的语言,那么城市之间就可以开展贸易活动了。两个城市 ui, vi 之间可以开展贸易活动当且仅当存在一种通用语 L 满足 ui 到 vi 最短路上的所有城市(包括 ui, vi),都会使用 L。
为了衡量语言统一工作的效果,国王想让你计算有多少对城市 (u, v) (u < v),他们之间可以开展贸易活动。
@solution@
分为 u, v 有祖先关系;u, v 无祖先关系两类统计。
有祖先关系,不妨假设 u 是 v 的祖先。
只需求出路径的某一端在 v 的子树中,向上延伸深度最小为多少。
深度最小就是 lca,自下而上更新即可。
无祖先关系,不妨假设 dfs 序中 u 在 v 前面。
一样的,路径的某一端在 v 的子树,此时路径另一端 dfs 序中需要在 v 前面。
但是不同的路径可能会重复经过某一个点,导致重复统计。
假设所有路径另一端点的点集为 S,我们取 S + {v} 到根的链的并集,然后扣掉 {v} 到根的点数,就可以得到答案。
链并集就是个经典问题:∑端点深度 - ∑dfs序中相邻点lca的深度。然后可以用线段树合并维护链并集。
如果用 O(logn) 的 lca,则总时间复杂度为 O(nlog^2n)。
当然还要在线段树中去掉 dfs 序在 v 后面的点。
@accepted code@
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
struct edge{
int to; edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = edges;
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
int fa[20][MAXN + 5], dep[MAXN + 5];
int dfn[MAXN + 5], tid[MAXN + 5], dcnt;
void dfs1(int x, int f) {
fa[0][x] = f;
for(int i=1;i<20;i++)
fa[i][x] = fa[i-1][fa[i-1][x]];
dep[x] = dep[f] + 1, dfn[++dcnt] = x, tid[x] = dcnt;
for(edge *p=adj[x];p;p=p->nxt)
if( p->to != f ) dfs1(p->to, x);
}
int lca(int u, int v) {
if( dep[u] < dep[v] ) swap(u, v);
for(int i=19;i>=0;i--)
if( dep[fa[i][u]] >= dep[v] )
u = fa[i][u];
if( u == v ) return u;
for(int i=19;i>=0;i--)
if( fa[i][u] != fa[i][v] )
u = fa[i][u], v = fa[i][v];
return fa[0][u];
}
int ch[2][20*MAXN + 5], lm[20*MAXN + 5], rm[20*MAXN + 5], ncnt;
ll sum[20*MAXN + 5]; int rt[20*MAXN + 5];
void pushup(int x) {
lm[x] = (lm[ch[0][x]] != -1 ? lm[ch[0][x]] : lm[ch[1][x]]);
rm[x] = (rm[ch[1][x]] != -1 ? rm[ch[1][x]] : rm[ch[0][x]]);
sum[x] = sum[ch[0][x]] + sum[ch[1][x]];
if( rm[ch[0][x]] != -1 && lm[ch[1][x]] != -1 )
sum[x] -= dep[lca(dfn[rm[ch[0][x]]], dfn[lm[ch[1][x]]])];
}
void update(int &x, int l, int r, int p, int d) {
if( !x ) x = (++ncnt), lm[x] = -1, rm[x] = -1;
if( l == r ) {
if( d == 1 ) {
if( lm[x] == -1 )
lm[x] = rm[x] = l, sum[x] = dep[dfn[l]];
} else if( d == -1 ) {
if( lm[x] != -1 )
lm[x] = rm[x] = -1, sum[x] = 0;
}
return ;
}
int m = (l + r) >> 1;
if( p <= m ) update(ch[0][x], l, m, p, d);
else update(ch[1][x], m + 1, r, p, d);
pushup(x);
}
int merge(int x, int y, int l, int r) {
if( !x || !y ) return x + y;
if( l == r ) {
if( lm[y] != -1 )
lm[x] = rm[x] = l, sum[x] = dep[dfn[l]];
return x;
}
int m = (l + r) >> 1;
ch[0][x] = merge(ch[0][x], ch[0][y], l, m);
ch[1][x] = merge(ch[1][x], ch[1][y], m + 1, r);
pushup(x); return x;
}
int n, m;
vector<int>v[MAXN + 5]; int mnd[MAXN + 5]; ll ans;
void dfs2(int x, int f) {
rt[x] = 0;
for(int i=0;i<v[x].size();i++)
update(rt[x], 1, n, tid[v[x][i]], 1);
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
dfs2(p->to, x), mnd[x] = min(mnd[x], mnd[p->to]);
rt[x] = merge(rt[x], rt[p->to], 1, n);
}
ans += (dep[x] - mnd[x]);
while( rm[rt[x]] >= tid[x] )
update(rt[x], 1, n, rm[rt[x]], -1);
ans += sum[rt[x]];
if( rm[rt[x]] != -1 ) ans -= dep[lca(x, dfn[rm[rt[x]]])];
}
int main() {
// freopen("language.in", "r", stdin);
// freopen("language.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i=1,x,y;i<n;i++)
scanf("%d%d", &x, &y), addedge(x, y);
dfs1(1, 0);
for(int i=1;i<=n;i++) mnd[i] = dep[i];
for(int i=1,s,t,l;i<=m;i++) {
scanf("%d%d", &s, &t), l = lca(s, t);
mnd[s] = min(mnd[s], dep[l]), mnd[t] = min(mnd[t], dep[l]);
if( tid[s] > tid[t] ) swap(s, t); v[t].push_back(s);
}
lm[0] = rm[0] = -1, dfs2(1, 0), printf("%lld\n", ans);
}
@details@
我竟然做出来一道ZJOI题?可能也只有这一道了吧
数组太多了可能会搞混,一定要区分清楚。
理论上最优复杂度是写 O(1) 的 lca,好在出题人没有卡(
@loj - 3046@「ZJOI2019」语言的更多相关文章
- 【线段树 树链剖分 差分 经典技巧】loj#3046. 「ZJOI2019」语言【未完】
还是来致敬一下那过往吧 题目分析 先丢代码 #include<bits/stdc++.h> ; ; ; struct node { int top,son,fa,tot; }a[maxn] ...
- 【LOJ】#3046. 「ZJOI2019」语言
LOJ#3046. 「ZJOI2019」语言 先orz zsy吧 有一个\(n\log^3n\)的做法是把树链剖分后,形成logn个区间,这些区间两两搭配可以获得一个矩形,求矩形面积并 然后就是对于一 ...
- Loj #3044. 「ZJOI2019」Minimax 搜索
Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ...
- Loj #3045. 「ZJOI2019」开关
Loj #3045. 「ZJOI2019」开关 题目描述 九条可怜是一个贪玩的女孩子. 这天,她和她的好朋友法海哥哥去玩密室逃脱.在他们面前的是 \(n\) 个开关,开始每个开关都是关闭的状态.要通过 ...
- Loj #3042. 「ZJOI2019」麻将
Loj #3042. 「ZJOI2019」麻将 题目描述 九条可怜是一个热爱打麻将的女孩子.因此她出了一道和麻将相关的题目,希望这题不会让你对麻将的热爱消失殆尽. 今天,可怜想要打麻将,但是她的朋友们 ...
- 「ZJOI2019」语言 解题报告
「ZJOI2019」语言 3个\(\log\)做法比较简单,但是写起来还是有点麻烦的. 大概就是树剖把链划分为\(\log\)段,然后任意两段可以组成一个矩形,就是个矩形面积并,听说卡卡就过去了. 好 ...
- bzoj5518 & loj3046 「ZJOI2019」语言 线段树合并+树链的并
题目传送门 https://loj.ac/problem/3046 题解 首先问题就是问有多少条路径是给定的几条路径中的一条的一个子段. 先考虑链的做法. 枚举右端点 \(i\),那么求出 \(j\) ...
- 「ZJOI2019」语言
传送门 Description 给定一棵\(n\)个点的树和\(m\)条链,两个点可以联会当且仅当它们同在某一条链上,求可以联会的点的方案数 \(n,m\leq10^5\) Solution 考虑计 ...
- @loj - 3043@「ZJOI2019」线段树
目录 @description@ @solution@ @accepted code@ @details@ @description@ 九条可怜是一个喜欢数据结构的女孩子,在常见的数据结构中,可怜最喜 ...
随机推荐
- [翻译]用于.NET Core的Windows窗体设计器发布
本文由微信公众号<开发者精选资讯>翻译首发,转载请注明来源 今天我们很高兴地宣布,.NET Core项目的Windows窗体设计器现在可以在 Visual Studio 2019 16.6 ...
- .Net基础之1——学前入门
1..Net平台 2.C#编程语言 3..Net都能做什么 Winform桌面应用程序.Internet应用程序——ASP.Net(京东.淘宝.携程网)(主推). WP8手机开发.Unity 3D游戏 ...
- JUC(4)---java线程池原理及源码分析
线程池,既然是个池子里面肯定就装很多线程. 如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁 线程,如此一来会大大降低系统的效率.可能出现服务器在为每个请求创建新线程和销毁 ...
- docker启动各种容器命令大全
安装步骤梳理 搜索镜像 拉取镜像 查看镜像 启动镜像 停止容器 移除容器 安装tomcat docker hub好查找tomcat镜像 docker search tomcat 从docker hu ...
- mysql中的联合查询(内联、左联、外联、右联、全联)
转 https://www.cnblogs.com/withscorpion/p/9454490.html 联合查询效率较高,举例子来说明联合查询:内联inner join .左联left oute ...
- FPGA内部硬件结构简介
我们知道FPGA内部有很多可供用户任意配置的资源,其中包括:可编程逻辑.可编程I/O.互连线.IP核等资源,很多学过数字电路的人都知道与或非门可以构成几乎所有的数字电路,但是FPGA内部最基本的主要单 ...
- spark学习笔记总结
Spark简介 spark 可以很容易和yarn结合,直接调用HDFS.Hbase上面的数据,和hadoop结合.配置很容易. spark发展迅猛,框架比hadoop更加灵活实用.减少了延时处理,提高 ...
- [FlashDevelop] 003.FlashDevelop + LayaFlash + Starling环境配置及实战
1.首先我们需要下载LayaStarling框架地址:http://layabox.com/index.php?m=content&c=index&a=lists&catid= ...
- 常用docker命令备忘录
查看镜像 docker images 查看运行中的容器 docker ps 删除镜像 docker rmi 容器id 直接删除所有镜像 docker rmi `docker images -q` 直接 ...
- jchdl - GSL实例:FullAdder
https://mp.weixin.qq.com/s/CtT08xZON0YxnheqDM2FAw 全加器是能够计算低位进位的二进制加法电路.与半加器相比,全加器不只考虑本位计算结果是否有进位,也考虑 ...