题解 P5327 [ZJOI2019]语言

无限接近于零,却又不等于零的可能性。

P5327 [ZJOI2019]语言

解题思路

暴力

首先讲一下我垃圾的 40pts 的暴力(其他 dalao 都是 60pts 起步):

当然评测机快的话(比如 LOJ 的),可以卡过 3,4 个点(逃。

对于 1,2 测试点的话,我们直接记录两个节点之间路径上的所有点,然后用一个二维数组存一下两个点是否能互相贸易。

最后暴力求 ans 就好了。。

然后我们看到了链的部分分,然后就是在序列上的处理了:

对于每一个操作,我们记录下左右端点,然后按照左端点为第一关键字,右端点为第二关键字进行排序。

把各个操作分成若干组,保证每一组的最左端的点比前一组的所有的右端点都要大。

然后对于不同组的第一个直接给答案加上\(C_{r-l+1}^2\),也就是\(\dfrac{(r-l)\times(r-l+1)}{2}\)

对于同一组的,如果该区间在本组此前的区间内,那么它就没有贡献。

否则把它在组内的长度乘上在组外的长度还有组外边长度的自由组合。

设此时这一组的右端点是 maxr,贡献就是\((r-maxr)\times(maxr-l)+\dfrac{(r-maxr)\times(r-maxr+1)}{2}\)

\(code\)

正解

正解的做法就比较神仙了,算法方面就是线段树合并+动态开点+树上差分,在加上一点虚树的思想。

对于每个点建一棵线段树,然后再树上差分线段树合并就是各个节点对于答案的贡献了,因此,现在的问题在于对每个节点的处理。

不难发现有以下性质:

对于所有可以与 x 贸易的点实际上就构成了一个生成树,也可以叫做联通块。

如果点 s 和 t 的路径会经过点 x ,那么我们称 s 和 t 为 x 的两个极远点,那么就有了以下结论:

x 的生成树大小其实就是能把 x 的所有极远点的最小生成树。

为了方便,我们硬点存在极远点 1 ,最后再除去 1 的贡献。

然后,如果我们现在需要把 y 点加入到 x 的生成树里,其实就是需要把该生成树里距离 y 最近的点与 y 连起来。

假设那个点是 z ,那么 y 的贡献就是\(dep_y-dep_{\operatorname{lca}(y,z)}\)

对于 1 点的贡献其实就是所有点的 lca 的 dep 之和。

在线段树上进行操作时,存储四个值:操作数,两个极远点,贡献

接下来就是转移了,因为线段树是建立在时间戳上的,所以对于一个区间来说,两个极远点一定分别来自左右两个子区间。

对于两个子区间在向上更新时不能单纯的只记为两个区间的加和,贡献还应该算上两个子区间之间最近的节点连接起来的贡献,也就是这两个节点的 lca 的 dep 值。

最后就是实现细节了:注意下时间戳和标号之间的转化就好了。

对于求 lca 的时候可以用 \(\mathcal{O}(1)\)查询的优秀 RMQ-ST 算法 但是貌似,树链剖分的 \(\mathcal{O}(n)\)预处理更快一些,其实都大同小异了。。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,M=N<<1;
int n,m,tot,ans,root[N];
int tim,dfn[N],id[N],siz[N],son[N],dep[N],topp[N],fa[N];
vector<int> del[N];
struct Edge
{
	int tot,head[N],nxt[M],ver[M];
	void add(int x,int y)
	{
		ver[++tot]=y;
		nxt[tot]=head[x];
		head[x]=tot;
	}
}e;
struct Vector_Tree
{
	int l,r,s,t,dat,f;
}tre[N*80];
void pre_dfs(int x)
{
	siz[x]=1;
	for(int i=e.head[x];i;i=e.nxt[i])
	{
		int to=e.ver[i];
		if(to==fa[x])
			continue;
		fa[to]=x;
		dep[to]=dep[x]+1;
		pre_dfs(to);
		siz[x]+=siz[to];
		if(siz[to]>siz[son[x]])
			son[x]=to;
	}
}
void pre_dfs(int x,int tp)
{
	topp[x]=tp;
	dfn[x]=++tim;
	id[tim]=x;
	if(son[x])
		pre_dfs(son[x],tp);
	for(int i=e.head[x];i;i=e.nxt[i])
		if(!dfn[e.ver[i]])
			pre_dfs(e.ver[i],e.ver[i]);
}
int LCA(int x,int y)
{
	if(!x||!y)	return 0;
	while(topp[x]^topp[y])
	{
		if(dep[topp[x]]<dep[topp[y]])
			swap(x,y);
		x=fa[topp[x]];
	}
	if(dep[x]>dep[y])
		swap(x,y);
	return x;
}
void push_up(int x)
{
	tre[x].s=(tre[tre[x].l].s?tre[tre[x].l].s:tre[tre[x].r].s);
	tre[x].t=(tre[tre[x].r].t?tre[tre[x].r].t:tre[tre[x].l].t);
	if(!tre[tre[x].l].t||!tre[tre[x].r].s)	tre[x].f=tre[tre[x].l].f+tre[tre[x].r].f;
	else	tre[x].f=tre[tre[x].l].f+tre[tre[x].r].f-dep[LCA(tre[tre[x].l].t,tre[tre[x].r].s)];
}
void insert(int &x,int l,int r,int pos,int num)
{
	if(!x)	x=++tot;
	if(l==r)
	{
		tre[x].dat+=num;
		tre[x].s=tre[x].t=(tre[x].dat?id[pos]:0);
		tre[x].f=(tre[x].dat?dep[id[pos]]:0);
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)	insert(tre[x].l,l,mid,pos,num);
	else	insert(tre[x].r,mid+1,r,pos,num);
	push_up(x);
}
void merge(int &x,int y,int l,int r)
{
	if(!x||!y)
	{
		x|=y;
		return ;
	}
	if(l==r)
	{
		tre[x].dat+=tre[y].dat;
		tre[x].f=(tre[x].f?tre[x].f:tre[y].f);
		tre[x].s=(tre[x].s?tre[x].s:tre[y].s);
		tre[x].t=(tre[x].t?tre[x].t:tre[y].t);
		return ;
	}
	int mid=(l+r)>>1;
	merge(tre[x].l,tre[y].l,l,mid);
	merge(tre[x].r,tre[y].r,mid+1,r);
	push_up(x);
}
int query(int x)
{
	return tre[x].f-dep[LCA(tre[x].s,tre[x].t)];
}
void redfs(int x)
{
	for(int i=e.head[x];i;i=e.nxt[i])
		if(e.ver[i]!=fa[x])
			redfs(e.ver[i]);
	for(int i=0;i<del[x].size();i++)
		insert(root[x],1,tim,dfn[del[x][i]],-1);
	ans+=query(root[x]);
	merge(root[fa[x]],root[x],1,tim);
}
#undef int
int main()
{
	#define int register long long
	#define ll long long
	scanf("%lld%lld",&n,&m);
	for(int i=1,x,y;i<n;i++)
	{
		scanf("%lld%lld",&x,&y);
		e.add(x,y);
		e.add(y,x);
	}
	pre_dfs(1);
	pre_dfs(1,1);
	for(int i=1,x,y;i<=m;i++)
	{
		scanf("%lld%lld",&x,&y);
		insert(root[x],1,tim,dfn[x],1);
		insert(root[x],1,tim,dfn[y],1);
		insert(root[y],1,tim,dfn[x],1);
		insert(root[y],1,tim,dfn[y],1);
		int lca=LCA(x,y);
		del[lca].push_back(x);
		del[lca].push_back(y);
		del[fa[lca]].push_back(x);
		del[fa[lca]].push_back(y);
	}
	redfs(1);
	printf("%lld\n",ans/2ll);
	return 0;
}
posted @ 2021-07-07 11:09  Varuxn  阅读(74)  评论(0编辑  收藏  举报