@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. redis集群复制和故障转移

    #### 一.集群的问题- 1.当某个主节点宕机后,对应的槽位没有节点承担,整个集群处于失败状态,不可用,怎么办- 2.如何判断某个主节点是否真正的岩机?- 3.如果从某个主节点的所有从节点中选举出一 ...

  2. sql语句 怎么从一张表中查询数据插入到另一张表中?

    sql语句 怎么从一张表中查询数据插入到另一张表中?  ----原文地址:http://www.phpfans.net/ask/MTc0MTQ4Mw.html 比如我有两张表 table1 字段 un ...

  3. Spring JSR-250 注释

    Spring还使用基于 JSR-250 注释,它包括 @PostConstruct 注释 @PreDestroy 注释 @Resource 注释 @PostConstruct 和 @PreDestro ...

  4. 关闭 WordPress 自动更新

    # 方法一 推荐!编辑 WordPress 网站目录下的 wp-config.php 文件,添加如下代码: define( 'AUTOMATIC_UPDATER_DISABLED', true ); ...

  5. 【IOS】点击按钮-去高亮-加点击效果

    (1)前提:html页面 ,手机端, (2)问题一: 在iphone上(貌似是9以后..),给div/span/...等元素上加onclick事件,根本不起作用,这是为啥捏? -- 在元素上加  cu ...

  6. Unity中激活子物体

    void GetChildrenAndSetActive() { Transform[] imageTargetObjects = GetComponentsInChildren<Transfo ...

  7. web自动化之执行js脚本

    from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from seleni ...

  8. HTTP 规范中的那些暗坑

    HTTP 协议可以说是开发者最熟悉的一个网络协议,「简单易懂」和「易于扩展」两个特点让它成为应用最广泛的应用层协议. 虽然有诸多的优点,但是在协议定义时因为诸多的博弈和限制,还是隐藏了不少暗坑,让人一 ...

  9. PIC16F887的LCD

    RS RA5 RW RA4 RD RA3 将引脚设置为输出的时候要对ANS5 ANS4 ANS3 设置为0

  10. JAVA自学笔记(2)

    Java跳跃级知识储备 1.Mathod新篇章 1.0进制转化(方法中的参数其实就是局部变量,在方法中定义的变量要赋初值) import java.util.Scanner; public class ...