[LOJ3046][ZJOI2019]语言:树链的并+线段树合并
分析
问题显然可以转化为对于每个节点询问所有这个节点的所有链的链并的大小。
考场上我直接通过树剖打标记+树剖线段树维护以\(O(n \log^3 n)\)的时间复杂度暴力实现了这个过程。(使用LCT或者全局平衡二叉树可以实现\(O(n \log^2 n)\)的时间复杂度)
考虑如何快速求出链并的大小,有这样一个结论:把所有的链的端点按dfs序排序后,链并的大小等于所有链的两端点的深度之和减去相邻端点的LCA的深度之和再减去所有端点的LCA的深度,这个结论(貌似)在链并是一个连通块的时候均成立。
有了这个结论,我们就可以快乐地线段树合并了,时间复杂度为\(O(n \log n)\)。
代码
考场代码(\(O(n \log^3 n)\))
这个算法可以过掉本题,但是无法通过UOJ上的HACK数据。
// 舞台赋予了我们生存的意义
// 让荣光停落于刀锋之上
// ——Star Divine -Finale-
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <utility>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define mkpr std::make_pair
#define fi first
#define se second
#define lowbit(a) ((a)&(-(a)))
typedef long long LL;
using std::cerr;
using std::endl;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int MAXN=100005;
int n,m,ecnt,head[MAXN];
struct Edge{
int to,nxt;
}e[MAXN<<1];
inline void add_edge(int bg,int ed){
++ecnt;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
}
int fa[MAXN],dep[MAXN],siz[MAXN],pc[MAXN],top[MAXN];
int tot,id[MAXN],num[MAXN],len[MAXN],toparr[MAXN],cnt;
void dfs1(int x,int pre,int dept){
fa[x]=pre;
dep[x]=dept;
siz[x]=1;
int maxsiz=-1;
trav(i,x){
int ver=e[i].to;
if(ver==pre)continue;
dfs1(ver,x,dept+1);
siz[x]+=siz[ver];
if(siz[ver]>maxsiz){
maxsiz=siz[ver];
pc[x]=ver;
}
}
}
void dfs2(int x,int topf){
top[x]=topf;
id[x]=++tot;
num[tot]=x;
++len[topf];
if(!pc[x])return;
dfs2(pc[x],topf);
trav(i,x){
int ver=e[i].to;
if(ver==fa[x]||ver==pc[x])continue;
toparr[++cnt]=ver;
dfs2(ver,ver);
}
}
int ss[MAXN],tt[MAXN];
std::vector<int> vec[MAXN];
inline void set_tag(int x,int y,int pid){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])std::swap(x,y);
vec[id[top[x]]].pb(pid);
vec[id[x]+1].pb(-pid);
x=fa[top[x]];
}
if(dep[x]>dep[y])std::swap(x,y);
vec[id[x]].pb(pid);
vec[id[y]+1].pb(-pid);
}
#define mid ((l+r)>>1)
int minn[MAXN<<2],mncnt[MAXN<<2],tag[MAXN<<2],ql,qr,kk;
int sgn,root[MAXN],lc[MAXN<<2],rc[MAXN<<2];
inline void push_tag(int o,int _kk){
minn[o]+=_kk;
tag[o]+=_kk;
}
inline void pushdown(int o){
if(!tag[o])return;
push_tag(lc[o],tag[o]);
push_tag(rc[o],tag[o]);
tag[o]=0;
}
inline void pushup(int o){
if(minn[lc[o]]<minn[rc[o]]){
minn[o]=minn[lc[o]];
mncnt[o]=mncnt[lc[o]];
}
else if(minn[lc[o]]>minn[rc[o]]){
minn[o]=minn[rc[o]];
mncnt[o]=mncnt[rc[o]];
}
else{
minn[o]=minn[lc[o]];
mncnt[o]=mncnt[lc[o]]+mncnt[rc[o]];
}
}
int build(int l,int r){
int o=++sgn;
if(l==r){
minn[o]=0;
mncnt[o]=1;
return o;
}
lc[o]=build(l,mid);
rc[o]=build(mid+1,r);
pushup(o);
return o;
}
void add(int o,int l,int r){
if(ql<=l&&r<=qr){
push_tag(o,kk);
return;
}
pushdown(o);
if(mid>=ql)add(lc[o],l,mid);
if(mid<qr)add(rc[o],mid+1,r);
pushup(o);
}
/*
void write(int o,int l,int r){
if(l==r){
cerr<<minn[o]<<" ";
return;
}
pushdown(o);
write(lc,l,mid);
write(rc,mid+1,r);
}
*/
#undef mid
int nowans;
inline void path_add(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])std::swap(x,y);
if(minn[root[top[x]]]==0)nowans-=mncnt[root[top[x]]];
ql=1,qr=id[x]-id[top[x]]+1;
add(root[top[x]],1,len[top[x]]);
if(minn[root[top[x]]]==0)nowans+=mncnt[root[top[x]]];
x=fa[top[x]];
}
if(dep[x]>dep[y])std::swap(x,y);
if(minn[root[top[x]]]==0)nowans-=mncnt[root[top[x]]];
ql=id[x]-id[top[x]]+1,qr=id[y]-id[top[y]]+1;
add(root[top[x]],1,len[top[x]]);
if(minn[root[top[x]]]==0)nowans+=mncnt[root[top[x]]];
}
/*
inline int calc(){
int ret=0;
rin(i,1,cnt)if(minn[root[toparr[i]]]==0)ret+=mncnt[root[toparr[i]]];
return ret;
}
*/
int main(){
freopen("language.in","r",stdin);
freopen("language.out","w",stdout);
n=read(),m=read();
rin(i,2,n){
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
toparr[++cnt]=1;
dfs1(1,0,1);
dfs2(1,1);
rin(i,1,cnt)root[toparr[i]]=build(1,len[toparr[i]]);
rin(i,1,m){
ss[i]=read(),tt[i]=read();
set_tag(ss[i],tt[i],i);
}
LL ans=0;nowans=n;
rin(i,1,n){
if(minn[root[top[num[i]]]]==0)nowans-=mncnt[root[top[num[i]]]];
ql=qr=i-id[top[num[i]]]+1,kk=1;
add(root[top[num[i]]],1,len[top[num[i]]]);
if(minn[root[top[num[i]]]]==0)nowans+=mncnt[root[top[num[i]]]];
rin(j,0,Size(vec[i])-1){
int pid=vec[i][j];
kk=1;
if(pid<0)pid=-pid,kk=-1;
path_add(ss[pid],tt[pid]);
}
ans+=n-nowans-1;
if(minn[root[top[num[i]]]]==0)nowans-=mncnt[root[top[num[i]]]];
ql=qr=i-id[top[num[i]]]+1,kk=-1;
add(root[top[num[i]]],1,len[top[num[i]]]);
if(minn[root[top[num[i]]]]==0)nowans+=mncnt[root[top[num[i]]]];
}
printf("%lld\n",ans>>1);
return 0;
}
/*
5 3
1 2
1 3
3 4
3 5
3 4
1 4
2 5
8
*/
正解(\(O(n \log n)\))
#include <bits/stdc++.h>
#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define mkpr std::make_pair
#define fi first
#define se second
#define lowbit(a) ((a)&(-(a)))
typedef long long LL;
using std::cerr;
using std::endl;
inline LL read(){
LL x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int MAXN=100005;
int n,m,ecnt,head[MAXN];
struct Edge{
int to,nxt;
}e[MAXN<<1];
inline void add_edge(int bg,int ed){
++ecnt;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
}
int fa[MAXN],dep[MAXN],siz[MAXN],pc[MAXN],top[MAXN];
int tot,len,id[MAXN],num[MAXN],pos[MAXN],st[20][MAXN<<1];
void dfs1(int x,int pre,int dept){
fa[x]=pre;
dep[x]=dept;
siz[x]=1;
int maxsiz=-1;
trav(i,x){
int ver=e[i].to;
if(ver==pre)continue;
dfs1(ver,x,dept+1);
siz[x]+=siz[ver];
if(siz[ver]>maxsiz){
maxsiz=siz[ver];
pc[x]=ver;
}
}
}
void dfs2(int x,int topf){
top[x]=topf;
id[x]=++tot;
num[tot]=x;
pos[x]=++len;
st[0][len]=id[x];
if(!pc[x])return;
dfs2(pc[x],topf);
st[0][++len]=id[x];
trav(i,x){
int ver=e[i].to;
if(ver==fa[x]||ver==pc[x])continue;
dfs2(ver,ver);
st[0][++len]=id[x];
}
}
void build_st(){
int lim=log2(len);
rin(i,1,lim)rin(j,1,len-(1<<i)+1)st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
inline int lca(int x,int y){
if(!x||!y)return 0;
x=pos[x],y=pos[y];
if(x>y)std::swap(x,y);
int lim=log2(y-x+1);
return num[std::min(st[lim][x],st[lim][y-(1<<lim)+1])];
}
int s[MAXN],t[MAXN];
std::vector<int> vec[MAXN];
int sgn,root[MAXN],lc[MAXN*40],rc[MAXN*40],lb[MAXN*40],rb[MAXN*40],cov[MAXN*40],loc;
LL sum[MAXN*40];
#define mid ((l+r)>>1)
inline void pushup(int o){
sum[o]=sum[lc[o]]+sum[rc[o]]-dep[lca(num[rb[lc[o]]],num[lb[rc[o]]])];
lb[o]=lb[lc[o]]>0?lb[lc[o]]:lb[rc[o]];
rb[o]=rb[rc[o]]>0?rb[rc[o]]:rb[lc[o]];
}
int insert(int pre,int l,int r){
int o=pre;
if(!o)o=++sgn;
if(l==r){
++cov[o];
sum[o]=dep[num[l]];
lb[o]=rb[o]=l;
return o;
}
if(loc<=mid)lc[o]=insert(lc[pre],l,mid);
else rc[o]=insert(rc[pre],mid+1,r);
pushup(o);
return o;
}
void erase(int o,int l,int r){
if(l==r){
cov[o]-=2;
if(!cov[o])sum[o]=lb[o]=rb[o]=0;
return;
}
if(loc<=mid)erase(lc[o],l,mid);
else erase(rc[o],mid+1,r);
pushup(o);
}
int merge(int x,int y,int l,int r){
if(!x||!y)return x+y;
if(l==r){
cov[x]+=cov[y];
sum[x]|=sum[y];
lb[x]|=lb[y];
rb[x]|=rb[y];
return x;
}
lc[x]=merge(lc[x],lc[y],l,mid);
rc[x]=merge(rc[x],rc[y],mid+1,r);
pushup(x);
return x;
}
#undef mid
LL ans;
void dfs3(int x){
trav(i,x){
int ver=e[i].to;
if(ver==fa[x])continue;
dfs3(ver);
root[x]=merge(root[x],root[ver],1,n);
}
rin(i,0,Size(vec[x])-1){
if(vec[x][i]<0){
loc=id[s[-vec[x][i]]];
erase(root[x],1,n);
loc=id[t[-vec[x][i]]];
erase(root[x],1,n);
}
}
rin(i,0,Size(vec[x])-1){
if(vec[x][i]>0){
loc=id[s[vec[x][i]]];
root[x]=insert(root[x],1,n);
loc=id[t[vec[x][i]]];
root[x]=insert(root[x],1,n);
}
}
if(!lb[root[x]])return;
int l=lca(num[lb[root[x]]],num[rb[root[x]]]);
ans+=sum[root[x]]-dep[fa[l]]-1;
}
int main(){
n=read(),m=read();
rin(i,2,n){
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
dfs1(1,0,1);
dfs2(1,1);
build_st();
rin(i,1,m){
s[i]=read(),t[i]=read();
int l=lca(s[i],t[i]);
vec[s[i]].pb(i);
vec[t[i]].pb(i);
vec[fa[l]].pb(-i);
}
dfs3(1);
printf("%lld\n",ans>>1);
return 0;
}
[LOJ3046][ZJOI2019]语言:树链的并+线段树合并的更多相关文章
- bzoj 4034 [HAOI2015] T2(树链剖分,线段树)
4034: [HAOI2015]T2 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1536 Solved: 508[Submit][Status] ...
- bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 10677 Solved: 4313[Submit ...
- poj 3237 Tree(树链剖分,线段树)
Tree Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 7268 Accepted: 1969 Description ...
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1272 Solved: 451[Submit][Status ...
- bzoj 2243 [SDOI2011]染色(树链剖分,线段树)
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4637 Solved: 1726[Submit][Status ...
- HDU 4366 Successor(树链剖分+zkw线段树+扫描线)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=4366 [题目大意] 有一个公司,每个员工都有一个上司,所有的人呈树状关系,现在给出每个人的忠诚值和 ...
- 【BZOJ3531】旅行(树链剖分,线段树)
[BZOJ3531]旅行(树链剖分,线段树) 题面 Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教 ...
- 【BZOJ5507】[GXOI/GZOI2019]旧词(树链剖分,线段树)
[BZOJ5507][GXOI/GZOI2019]旧词(树链剖分,线段树) 题面 BZOJ 洛谷 题解 如果\(k=1\)就是链并裸题了... 其实\(k>1\)发现还是可以用类似链并的思想,这 ...
- [bzoj4196][Noi2015]软件包管理器_树链剖分_线段树
软件包管理器 bzoj-4196 Noi-2015 题目大意:Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件 ...
- 【洛谷5439】【XR-2】永恒(树链剖分,线段树)
[洛谷5439][XR-2]永恒(树链剖分,线段树) 题面 洛谷 题解 首先两个点的\(LCP\)就是\(Trie\)树上的\(LCA\)的深度. 考虑一对点的贡献,如果这两个点不具有祖先关系,那么这 ...
随机推荐
- Luogu P2617 Dynamic Rankings(整体二分)
题目 动态区间第K小模板题. 一个非常可行的办法是BIT套动态开点权值SegTree,但是它跑的实在太慢了. 然后由于这题并没有强制在线,所以我们可以使用整体二分来吊打树套树. 当然如果强制在线的话就 ...
- Python简单主机批量管理工具
一.程序介绍 需求: 简单主机批量管理工具 需求: 1.主机分组 2.主机信息使用配置文件 3.可批量执行命令.发送文件,结果实时返回 4.主机用户名密码.端口可以不同 5.执行远程命令使用param ...
- php重定向的三种方法分享
一.用HTTP头信息 也就是用PHP的HEADER函数.PHP里的HEADER函数的作用就是向浏览器发出由HTTP协议规定的本来应该通过WEB服务器的控制指令,例如: 声明返回信息的类型(" ...
- 06 Python之列表和元组
1. 什么是列表 定义: 能装对象的对象 在python中使用[]来描述列表, 内部元素用逗号隔开. 对数据类型没有要求 列表存在索引和切片. 和字符串是一样的. 2. 相关的增删改查操作 添加: 1 ...
- 转载:Cesium的Property机制总结
转自:https://www.jianshu.com/p/f0b47997224c 前言 Cesium官方教程中有一篇叫<空间数据可视化>(Visualizing Spatial Data ...
- [转自SA]浅谈nginx的工作原理和使用
nginx apache 简单对比 nginx 相对 apache 的优点: 轻量级,同样起web 服务,比apache 占用更少的内存及资源 抗并发,nginx 处理请求是异步非阻塞的,而 apac ...
- DiffUtil和LiveData使用时遇到的问题
android在28之后换成了androidx,在此之前也可以用到这些功能,不过是引入的包不同,写法也有一些小的差别.我们之前的代码使用的是BaseQuickAdapter,所以不可以直接使用JetP ...
- 微软商店(Microsoft store)删除之后恢复,一行代码搞定
首先以管理员身份运行Windows PowerShell 地址C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\P ...
- 10.1、LNMT架构
Java环境安装包下载路径: https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.htm ...
- iperf和iperf3详解
一.iperf server端: iperf -s -p 25001 -B 192.168.33.103 (-u) -s 指定server端 -p 指定端口(要和客户端一致) -B 绑定ip地址 - ...