@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」语言的更多相关文章

  1. 【线段树 树链剖分 差分 经典技巧】loj#3046. 「ZJOI2019」语言【未完】

    还是来致敬一下那过往吧 题目分析 先丢代码 #include<bits/stdc++.h> ; ; ; struct node { int top,son,fa,tot; }a[maxn] ...

  2. 【LOJ】#3046. 「ZJOI2019」语言

    LOJ#3046. 「ZJOI2019」语言 先orz zsy吧 有一个\(n\log^3n\)的做法是把树链剖分后,形成logn个区间,这些区间两两搭配可以获得一个矩形,求矩形面积并 然后就是对于一 ...

  3. Loj #3044. 「ZJOI2019」Minimax 搜索

    Loj #3044. 「ZJOI2019」Minimax 搜索 题目描述 九条可怜是一个喜欢玩游戏的女孩子.为了增强自己的游戏水平,她想要用理论的武器武装自己.这道题和著名的 Minimax 搜索有关 ...

  4. Loj #3045. 「ZJOI2019」开关

    Loj #3045. 「ZJOI2019」开关 题目描述 九条可怜是一个贪玩的女孩子. 这天,她和她的好朋友法海哥哥去玩密室逃脱.在他们面前的是 \(n\) 个开关,开始每个开关都是关闭的状态.要通过 ...

  5. Loj #3042. 「ZJOI2019」麻将

    Loj #3042. 「ZJOI2019」麻将 题目描述 九条可怜是一个热爱打麻将的女孩子.因此她出了一道和麻将相关的题目,希望这题不会让你对麻将的热爱消失殆尽. 今天,可怜想要打麻将,但是她的朋友们 ...

  6. 「ZJOI2019」语言 解题报告

    「ZJOI2019」语言 3个\(\log\)做法比较简单,但是写起来还是有点麻烦的. 大概就是树剖把链划分为\(\log\)段,然后任意两段可以组成一个矩形,就是个矩形面积并,听说卡卡就过去了. 好 ...

  7. bzoj5518 & loj3046 「ZJOI2019」语言 线段树合并+树链的并

    题目传送门 https://loj.ac/problem/3046 题解 首先问题就是问有多少条路径是给定的几条路径中的一条的一个子段. 先考虑链的做法. 枚举右端点 \(i\),那么求出 \(j\) ...

  8. 「ZJOI2019」语言

    传送门 Description 给定一棵\(n\)个点的树和\(m\)条链,两个点可以联会当且仅当它们同在某一条链上,求可以联会的点的方案数 \(n,m\leq10^5\) Solution  考虑计 ...

  9. @loj - 3043@「ZJOI2019」线段树

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 九条可怜是一个喜欢数据结构的女孩子,在常见的数据结构中,可怜最喜 ...

随机推荐

  1. MVC4.0接口学习

    /// <summary> /// 正则验证身份证号是否合法 /// </summary> /// <param name="sIdCard"> ...

  2. FTP上传 -首先上传文件到的那台电脑得安装ftp

    /*↓↓↓↓ add upload ftp file 2014-03-16*/        /*↓↓↓↓ add 2014-03-16 ftp upload file*/    var $ftpse ...

  3. mysql全方位知识大盘点

    一.mysql都有哪些存储引擎?各自的特点是什么? 引擎 事务 锁 主键 索引 外键 数据结构 适用场景 InnoDB 支持 行锁.表锁 必须有主键,没有设置会自动创建 主键索引和数据在一起,其他索引 ...

  4. LaunchScreen作为启动图设置,修改无效的解决方案

    原有的推流APP用launchScreen做的启动图,现在要修改一张,发现修改无效. 当前测试的方法有 1,重启Xcode  卸载app 清楚xcode缓存 2,修改launchScreen.stor ...

  5. windows 10 2016 企业版 长期服务 激活方式

    试了很多,失败. 使用这个ok———————————————————————————————— 使用方式: 2.1.下载AAct.exe  https://www.baidu.com/link?url ...

  6. Js 改变时间格式输出格式

    朋友看到的方法,非js原生的 自己封装到 function date2str(x,y) { var z={y:x.getFullYear(),M:x.getMonth()+1,d:x.getDate( ...

  7. 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?

    写在前面 先说点题外话:不少读者工作几年后,仍然在使用Java7之前版本的方法,对于Java8版本的新特性,甚至是Java7的新特性几乎没有接触过.真心想对这些读者说:你真的需要了解下Java8甚至以 ...

  8. 前端练手小项目——网页版qq音乐仿写

    qq音乐网页版仿写 一些步骤与注意事项 一开始肯定就是html+css布局和页面了,这段特别耗时间,耐心写完就好了 首先要说一下大致流程: 一定要先布局html!,所以一定要先分析页面布局情况,用不同 ...

  9. 2020本科校招-从小白到拿到30k offer的学习经历

    本文是个人的2020年年中总结 还有十几天就要毕业,面临着身份从学生到互联网社畜的转变,未来的一切捉摸不定,但凡心中万千情绪,也只能「但行好事,莫问前程」. 介绍下博主背景:计算机本科大四,刚进大三时 ...

  10. vc程序设计--对键盘与鼠标的响应(1)

    // WindowsProject2.cpp : 定义应用程序的入口点. // /** * 当按下CTRL键时输出信息:CTRL键被压下 * 当按下K键时输出信息:K键被压下 * 当按下CTRL+K键 ...