[CF1486F]Pairs of Paths

本文介绍了使用树状数组和线段树解决具有唯一交点的路径问题,详细阐述了两种特殊情况及对应的解题策略。通过建立线段树并采用容斥原理统计交点数量,实现了O(nlogn)的时间复杂度解决方案。同时,文章提到了树状数组的常数优化可能,并提供了完整的C++源代码实现。
摘要由CSDN通过智能技术生成

Pairs of Paths

题解

完了,跑得还没有常数大师快

首先我们可以证明一点,只有一下两种情况会使得两条路径有且只有一个交点。
在这里插入图片描述
这种情况下 u 1 , v 1 , u 2 , v 2 u_{1},v_1,u_2,v_2 u1,v1,u2,v2分别来自 r t rt rt的不同儿子。
在这里插入图片描述
这种情况下, u 1 , v 1 , u 2 u_1,v_1,u_2 u1,v1,u2都来自 r t rt rt的某个儿子 v 2 v_2 v2 r t rt rt的一个祖先的某个儿孙或祖先它自己。
在这两种情况下,两条路径的的交点必定位于其中一条路径的 l c a lca lca处。

先考虑第一种情况,这种情况四个点都来自 r t rt rt不同的儿子的子树上,如果有与 r t rt rt本身相同的我们可以可以将其看作它的一个特殊的儿子。
我们可以将每条路径看作一个三元组 ( u , v , l c a ) (u,v,lca) (u,v,lca),将这个三元组加在 l c a lca lca节点上。
如何统计与三元组 ( u , v , l c a ) (u,v,lca) (u,v,lca)有第一种形式相交的路径,我们可以先加上所有 l c a lca lca在这个儿子的路径,再容斥减去与其同一个儿子来源的路径。设 u u u来自儿子 a a a v v v来自儿子 b b b,那么与这条路径相交的节点有 t o t a l − s u m u − s u m v + s u m u , v total-sum_{u}-sum_{v}+sum_{u,v} totalsumusumv+sumu,v
注意有端点在这个 l c a lca lca的节点的路径,他在 l c a lca lca上的端点与别人相交是合法的,遇到这种情况就没必要进行对应的容斥。

那么对于第二种情况,很容易发现有一条路径的 l c a lca lca是另一条的祖先,也就是枚举到下面这个路径的 l c a lca lca时必定已经dfs上面这个路径的 l c a lca lca
我们可以考虑利用dfs序建立一棵线段树,枚举到当前节点时将 l c a lca lca在这个节点上的线段的两个端点在线段树上 + 1 +1 +1,离开时 − 1 -1 1
这样在枚举到某个子节点时它的祖先的的线段必定都已加入,如果有一个端点在这个子节点的子树中,就必定与这个节点上的线段相交,那么对于这个节点上的线段,我们也可以采用容斥统计与它有第二种情况相交的线段数。
对于三元组 ( u , v , l c a ) (u,v,lca) (u,v,lca),先加上线段树上 l c a lca lca子树区间内的和,再分别减去 u , v u,v u,v两者对应的儿子的子树内的区间和,就可以得到与它有第二种情况相交的线段数。
由于之前加入线段最多只有一个端点在 l c a lca lca的子树内,没必要进行容斥,直接加上 s u m l c a − s u m a − s u m b sum_{lca}-sum_{a}-sum_{b} sumlcasumasumb即可。
同样需要注意有端点就是 l c a lca lca的线段的情况,避免减多了。

对于一条路径只有一个点的情况,我们需要特殊考虑一下,因为这种情况下只要经过这个点的线段就与它符合条件。

时间复杂度 O ( n l o g   n ) O\left(nlog\,n\right) O(nlogn)
妹儿的树状数组做法好像常数会小些,但复杂度是一样的

源码

话说为什么我现在都喜欢用dosaka来代替dfs呀

#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define lowbit(x) (x&-x)
#define reg register
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=1000000000000LL;
const LL mo=1e9+7;
const LL inv2=5e8+4;
const LL jzm=2333;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int n,m,head[MAXN],tot,dep[MAXN],f[MAXN][22],dfn[MAXN],idx;
LL ans;int sum[MAXN],val[MAXN],tr[MAXN<<2],ld[MAXN],rd[MAXN];
map<int,int>mp[MAXN];
struct ming{int a,b;};
vector<ming>vec[MAXN];
struct edge{int to,nxt;}e[MAXN<<1];
inline void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void dosaka(int u,int fa){
	dep[u]=dep[fa]+1;f[u][0]=fa;dfn[u]=++idx;ld[u]=idx;
	for(reg int i=1;i<20;i++)f[u][i]=f[f[u][i-1]][i-1];
	for(reg int i=head[u];i;i=e[i].nxt)if(e[i].to!=fa)dosaka(e[i].to,u);rd[u]=idx;
}
int lca(int u,int v){
	if(dep[u]>dep[v])swap(u,v);
	for(reg int i=19;i>=0;i--)if(dep[f[v][i]]>=dep[u])v=f[v][i];
	if(u==v)return u;
	for(reg int i=19;i>=0;i--)if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
	return f[u][0];
}
int FindFa(int u,int dp){for(reg int i=19;i>=0;i--)if(dp&(1<<i))u=f[u][i];return u;}
void insert(int rt,int l,int r,int ai,int aw){
	if(l>r||l>ai||r<ai)return ;tr[rt]+=aw;
	if(l==r)return ;int mid=l+r>>1;
	if(ai<=mid)insert(rt<<1,l,mid,ai,aw);
	else insert(rt<<1|1,mid+1,r,ai,aw);
}
int query(int rt,int l,int r,int al,int ar){
	if(l>r||l>ar||r<al)return 0;int mid=l+r>>1;
	if(al<=l&&r<=ar)return tr[rt];int res=0;
	if(al<=mid)res+=query(rt<<1,l,mid,al,ar);
	if(ar>mid)res+=query(rt<<1|1,mid+1,r,al,ar);
	return res;
}
void dosaka1(int u,int fa){
	for(reg int i=0;i<(int)vec[u].size();i++)insert(1,1,n,dfn[vec[u][i].a],1),insert(1,1,n,dfn[vec[u][i].b],1);
	for(reg int i=head[u];i;i=e[i].nxt)if(e[i].to!=fa)dosaka1(e[i].to,u);//,sum[e[i].to]=0;
	for(reg int i=0;i<(int)vec[u].size();i++){
		int a=vec[u][i].a,b=vec[u][i].b;
		if(a!=u)a=FindFa(a,dep[a]-dep[u]-1);if(b!=u)b=FindFa(b,dep[b]-dep[u]-1);
		ans+=1ll*i;if(a!=u)ans-=sum[a],sum[a]++;if(b!=u)ans-=sum[b],sum[b]++;
		if(a>b)swap(a,b);if(a!=u&&b!=u)ans+=mp[a][b],mp[a][b]++;
		//printf("dosaka %d %d %d:%d %d %d %d\n",u,a,b,sum[a],sum[b],mp[a][b],i-sum[a]-sum[b]+mp[a][b]);
	}
	ans+=1ll*val[u]*(val[u]-1)/2LL;ans+=1ll*val[u]*(int)vec[u].size();
	for(reg int i=0;i<(int)vec[u].size();i++)insert(1,1,n,dfn[vec[u][i].a],-1),insert(1,1,n,dfn[vec[u][i].b],-1);
	int summ=query(1,1,n,ld[u],rd[u]);ans+=1ll*summ*val[u];
	for(reg int i=head[u];i;i=e[i].nxt)if(e[i].to!=fa)sum[e[i].to]=query(1,1,n,ld[e[i].to],rd[e[i].to]);
	for(reg int i=0;i<(int)vec[u].size();i++){
		int tmp=summ,a=vec[u][i].a,b=vec[u][i].b,x;
		if(a!=u)x=FindFa(a,dep[a]-dep[u]-1),tmp-=sum[x];
		if(b!=u)x=FindFa(b,dep[b]-dep[u]-1),tmp-=sum[x];
		ans+=1ll*tmp;
	}
}
signed main(){
	read(n);
	for(reg int i=1;i<n;i++){
		int u,v;read(u);read(v);
		addEdge(u,v);addEdge(v,u);
	}
	dosaka(1,0);read(m);
	for(reg int i=1;i<=m;i++){
		int u,v;read(u);read(v);if(u==v){val[u]++;continue;}
		vec[lca(u,v)].push_back((ming){u,v});
	}
	dosaka1(1,0);printf("%lld\n",ans);
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值