[loj3046]语言
定义$S_{i}$表示第$i$条链所包含的点的集合,$(x,y)$合法当且仅当$x\ne y$且$\exists i,\{x,y\}\subseteq S_{i}$(答案即$\frac{合法点对数}{2}$),显然后者等价于$y\in \cup_{x\in S_{i}}S_{i}$,因此合法点对数为$\sum_{x=1}^{n}|\cup_{x\in S_{i}}S_{i}|-1$
结论:$链并的大小=链端点所构成的虚树点数=\frac{按照dfs序排序后相邻(包括首尾)两点距离和}{2}+1$
前者显然,后者证明如下:
对每一条边统计经过次数,设其连结的深度较大的点为$x$,那么记$p_{i}=1$当且仅当$i$在$x$子树内(否则$p_{i}=0$),观察可得两个点$x$和$y$经过这条边当且仅当$p_{x}+p_{y}=1$
考虑dfs序的性质:每一个子树一定是一段区间,因此设端点按dfs序排序后为$a_{1},a_{2},...,a_{k}$,$S=\{i|p_{a_{i}}=1\}$一定是一段区间$[l,r]$,观察可得当$[l,r]=\emptyset$或$[l,r]=[1,k]$时该边答案为0,否则答案为2
考虑$[l,r]=\emptyset$或$[l,r]=[1,k]$的条件,即等价于这条边不在虚树上,那么$\frac{按照dfs序排序后相邻(包括首尾)两点距离和}{2}$即为边数,根据树的性质,加1即为点数
根据这个结论,将每条链差分并用线段树合并来找到所有端点,线段树上维护:1.个数(判断是否存在);2.区间最小点;3.区间最大点;4.区间相邻点距离和(最左和最右可以在外面算)即可,如果用st表维护lca可以做到$o(n\log_{2}n)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include<bits/stdc++.h>
2 using namespace std;
3 #define N 100005
4 #define mid (l+r>>1)
5 struct ji{
6 int nex,to;
7 }edge[N<<1];
8 int V,E,n,m,x,y,head[N],dfn[N],id[N],s[N],f[N][21],r[N],ls[N*100],rs[N*100],vis[N*100],mn[N*100],mx[N*100],sum[N*100];
9 long long ans;
10 void add(int x,int y){
11 edge[E].nex=head[x];
12 edge[E].to=y;
13 head[x]=E++;
14 }
15 void dfs(int k,int fa,int sh){
16 dfn[k]=++x;
17 id[x]=k;
18 s[k]=sh;
19 f[k][0]=fa;
20 for(int i=1;i<=20;i++)f[k][i]=f[f[k][i-1]][i-1];
21 for(int i=head[k];i!=-1;i=edge[i].nex)
22 if (edge[i].to!=fa)dfs(edge[i].to,k,sh+1);
23 }
24 int lca(int x,int y){
25 if (s[x]<s[y])swap(x,y);
26 for(int i=20;i>=0;i--)
27 if (s[f[x][i]]>=s[y])x=f[x][i];
28 if (x==y)return x;
29 for(int i=20;i>=0;i--)
30 if (f[x][i]!=f[y][i]){
31 x=f[x][i];
32 y=f[y][i];
33 }
34 return f[x][0];
35 }
36 int dis(int x,int y){
37 return s[x]+s[y]-2*s[lca(x,y)];
38 }
39 void up(int k){
40 mn[k]=min(mn[ls[k]],mn[rs[k]]);
41 mx[k]=max(mx[ls[k]],mx[rs[k]]);
42 sum[k]=sum[ls[k]]+sum[rs[k]];
43 if ((mx[ls[k]])&&(mn[rs[k]]<=n))sum[k]+=dis(id[mx[ls[k]]],id[mn[rs[k]]]);
44 }
45 void update(int &k,int l,int r,int x,int y){
46 if (!k){
47 k=++V;
48 mn[k]=n+1;
49 }
50 if (l==r){
51 vis[k]+=y;
52 if (vis[k]>0)mn[k]=mx[k]=l;
53 else{
54 mn[k]=n+1;
55 mx[k]=0;
56 }
57 return;
58 }
59 if (x<=mid)update(ls[k],l,mid,x,y);
60 else update(rs[k],mid+1,r,x,y);
61 up(k);
62 }
63 int merge(int k1,int k2){
64 if ((!k1)||(!k2))return k1+k2;
65 if ((!ls[k1])&&(!rs[k1])){
66 vis[k1]+=vis[k2];
67 if (vis[k1]>0){
68 mn[k1]=min(mn[k1],mn[k2]);
69 mx[k1]=max(mx[k1],mx[k2]);
70 }
71 else{
72 mn[k1]=n+1;
73 mx[k1]=0;
74 }
75 return k1;
76 }
77 ls[k1]=merge(ls[k1],ls[k2]);
78 rs[k1]=merge(rs[k1],rs[k2]);
79 up(k1);
80 return k1;
81 }
82 void dfs(int k,int fa){
83 for(int i=head[k];i!=-1;i=edge[i].nex)
84 if (edge[i].to!=fa){
85 dfs(edge[i].to,k);
86 r[k]=merge(r[k],r[edge[i].to]);
87 }
88 if (mn[r[k]]!=mx[r[k]])ans+=sum[r[k]]+dis(id[mn[r[k]]],id[mx[r[k]]]);
89 }
90 int main(){
91 scanf("%d%d",&n,&m);
92 memset(head,-1,sizeof(head));
93 for(int i=1;i<n;i++){
94 scanf("%d%d",&x,&y);
95 add(x,y);
96 add(y,x);
97 }
98 x=0;
99 dfs(1,0,1);
100 mn[0]=n+1;
101 for(int i=1;i<=m;i++){
102 scanf("%d%d",&x,&y);
103 int z=lca(x,y);
104 update(r[x],1,n,dfn[x],1);
105 update(r[x],1,n,dfn[y],1);
106 update(r[y],1,n,dfn[x],1);
107 update(r[y],1,n,dfn[y],1);
108 update(r[f[z][0]],1,n,dfn[x],-2);
109 update(r[f[z][0]],1,n,dfn[y],-2);
110 }
111 dfs(1,0);
112 printf("%lld",ans/4);
113 }
[loj3046]语言的更多相关文章
- [LOJ3046][ZJOI2019]语言:树链的并+线段树合并
分析 问题显然可以转化为对于每个节点询问所有这个节点的所有链的链并的大小. 考场上我直接通过树剖打标记+树剖线段树维护以\(O(n \log^3 n)\)的时间复杂度暴力实现了这个过程.(使用LCT或 ...
- bzoj5518 & loj3046 「ZJOI2019」语言 线段树合并+树链的并
题目传送门 https://loj.ac/problem/3046 题解 首先问题就是问有多少条路径是给定的几条路径中的一条的一个子段. 先考虑链的做法. 枚举右端点 \(i\),那么求出 \(j\) ...
- C语言 · 高精度加法
问题描述 输入两个整数a和b,输出这两个整数的和.a和b都不超过100位. 算法描述 由于a和b都比较大,所以不能直接使用语言中的标准数据类型来存储.对于这种问题,一般使用数组来处理. 定义一个数组A ...
- Windows server 2012 添加中文语言包(英文转为中文)(离线)
Windows server 2012 添加中文语言包(英文转为中文)(离线) 相关资料: 公司环境:亚马孙aws虚拟机 英文版Windows2012 中文SQL Server2012安装包,需要安装 ...
- iOS开发系列--Swift语言
概述 Swift是苹果2014年推出的全新的编程语言,它继承了C语言.ObjC的特性,且克服了C语言的兼容性问题.Swift发展过程中不仅保留了ObjC很多语法特性,它也借鉴了多种现代化语言的特点,在 ...
- C语言 · Anagrams问题
问题描述 Anagrams指的是具有如下特性的两个单词:在这两个单词当中,每一个英文字母(不区分大小写)所出现的次数都是相同的.例如,"Unclear"和"Nuclear ...
- C语言 · 字符转对比
问题描述 给定两个仅由大写字母或小写字母组成的字符串(长度介于1到10之间),它们之间的关系是以下4中情况之一: 1:两个字符串长度不等.比如 Beijing 和 Hebei 2:两个字符串不仅长度相 ...
- JAVA语言中的修饰符
JAVA语言中的修饰符 -----------------------------------------------01--------------------------------------- ...
- Atitit 项目语言的选择 java c#.net php??
Atitit 项目语言的选择 java c#.net php?? 1.1. 编程语言与技术,应该使用开放式的目前流行的语言趋势1 1.2. 从个人职业生涯考虑,java优先1 1.3. 从项目实际来 ...
随机推荐
- vue常见的三种组件通讯—props,$refs,this.$emit
一.父组件--->子组件 props 1.特点:props是用于父组件向子组件传递数据信息(props是单向绑定的,即只能父组件向子组件传递,不能反向 2.用法:父组件中使用子组件时,绑定要传递 ...
- Java爬虫系列四:使用selenium-java爬取js异步请求的数据
在之前的系列文章中介绍了如何使用httpclient抓取页面html以及如何用jsoup分析html源文件内容得到我们想要的数据,但是有时候通过这两种方式不能正常抓取到我们想要的数据,比如看如下例子. ...
- Win7恢复注册表
前言 安装仿真实验环境的时候,按照指引把杀软关了,然后出现了"不是有效Win32应用程序"的错误,bd了一下解决方案,爬到了一篇文章:删除注册表中的.exe然后重启,然后,所有的e ...
- 源码解析-Abp vNext丨LocalEventBus
前言 基础篇已经更新完了,从本篇开始我们进入,中级篇(学习部分源代码)我会挑一些我个人认为比较重要的知识点结合部分开源项目进行源码讲解,咱们废话不说直接上车. Abp vNext的事件总线分2种,一种 ...
- Golang通脉之接口
接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口类型 在Go语言中接口(interface)是一种类型,一种抽象的类型. interface是 ...
- CanalAdmin搭建Canal Server集群
CanalAdmin搭建Canal Server集群 一.背景 二.机器情况 三.实现步骤 1.下载canal admin 2.配置canalAdmin 3.初始化canal admin数据库 4.启 ...
- elasticsearch的索引操作
1.创建索引(test_index) curl -XPUT "http://192.168.99.1:9200/test_index" 2.创建索引,指定分片和副本的数量 curl ...
- Noip模拟79 2021.10.17(题目名字一样)
T1 F 缩点缩成个$DAG$,然后根据每个点的度数计算期望值 1 #include<cstdio> 2 #include<cstring> 3 #include<vec ...
- C++学习笔记之pimpl用法详解
原文链接:https://www.jb51.net/article/122557.htm 在编写稳定代码是,管理好代码间的依赖性是不可缺少的一个环节.特别是库文件的编写中,减少代码间的依赖性可以提供一 ...
- vs编译问题总结
编译遇到MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the ...