ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[提高组集训2021] 超级加倍

2021-11-10 21:32:16  阅读:175  来源: 互联网

标签:重构 树上 int tt 路径 加倍 2021 include 集训


一、题目

我们认为 \(x\rightarrow y\) 的简单路径是好的,当且仅当路径上的点最小的是 \(x\),最大的是 \(y\)

给出一棵 \(n\) 个点的树,求出好的简单路径条数。

\(n\leq 2\cdot 10^6\)

二、解法

很容易写出暴力点分治,但是因为需要解决二维偏序问题所以是 \(O(n\log^2n)\) 的。

首先考虑树是一条链的情况,发现可以二分求出 \(i\) 的范围,然后在这个范围中看有多少 \(j\) 即可。

那么搬到树上我们需要解决求出范围这个问题,既然它又是路径最值问题我们可以使用重构树,我们从大到小加入节点,对于新加的节点 \(u\),我们看所有边 \((u,v)\) 并且 \(v\) 已经被加入了,那么我们合并 \(u\) 和 \(v\) 连通块的根,并且把 \(v\) 并查集的根设置成 \(u\),可以用并查集简单维护。

那么重构树上两个点的 \(\tt lca\) 就是它们真实路径上的最小值,用类似的方法可以求出第二棵树使得 \(\tt lca\) 就是它们真实路径上的最大值。那么条件转化成第一棵树上 \(x\) 是 \(y\) 的祖先,第二棵树上 \(y\) 是 \(x\) 的祖先,我们可以求出第一棵树上的 \(\tt dfn\) 序,在第二棵树上 \(\tt dfs\),用树状数组来统计答案,时间复杂度 \(O(n\log n)\)

三、总结

对重构树的理解不仅仅是最小生成树上的边重构,还可以是本题的点重构。总之就是解决一个范围的问题,也就是满足某一条件的点在树上有特定的范围(比如子树)

//I just gonna burn the stars...
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 2000005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,tot,t1,t2,rt[2],a[M],b[M],f[M],f1[M],f2[M];
int fa[M],vis[M],in[M],out[M];long long ans;
struct edge{int v,next;}e[M<<1],e1[M],e2[M];
int find(int x)
{
	if(x!=fa[x]) fa[x]=find(fa[x]);
	return fa[x];
}
void build(int F)
{
	for(int i=1;i<=n;i++) fa[i]=i,vis[i]=0;
	for(int i=1;i<=n;i++)
	{
		int u=a[i];vis[u]=1;
		for(int j=f[u];j;j=e[j].next)
		{
			int v=e[j].v;
			if(vis[v])
			{
				if(F==0) e1[++t1]=edge{find(v),f1[u]},f1[u]=t1;
				else e2[++t2]=edge{find(v),f2[u]},f2[u]=t2;
				fa[find(v)]=u;
			}
		}
	}
	rt[F]=find(1);
}
void dfs1(int u)
{
	in[u]=++m;
	for(int i=f1[u];i;i=e1[i].next) dfs1(e1[i].v);
	out[u]=m;
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int c)
{
	for(int i=x;i<=n;i+=lowbit(i))
		b[i]+=c;
}
int ask(int x)
{
	int r=0;
	for(int i=x;i>0;i-=lowbit(i))
		r+=b[i];
	return r;
}
void dfs2(int u)
{
	add(in[u],1);
	ans+=ask(out[u])-ask(in[u]);
	for(int i=f2[u];i;i=e2[i].next) dfs2(e2[i].v);
	add(in[u],-1);
}
signed main()
{
	freopen("charity.in","r",stdin);
	freopen("charity.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=i;
		int j=read();if(!j) continue;
		e[++tot]=edge{i,f[j]},f[j]=tot;
		e[++tot]=edge{j,f[i]},f[i]=tot;
	}
	build(0);reverse(a+1,a+1+n);
	build(1);dfs1(rt[0]);dfs2(rt[1]);
	printf("%lld\n",ans);
}

标签:重构,树上,int,tt,路径,加倍,2021,include,集训
来源: https://www.cnblogs.com/C202044zxy/p/15535773.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有