「ROI 2016 Day2」快递

「ROI 2016 Day2」快递

先贴一下 mzz 的题解:https://www.cnblogs.com/chasedeath/p/12405160.html


这篇题解可能算是对上面题解的一些补充

mzz 的题解里代码已经被 hack 了,但他没把新的代码贴上去。

定义一条路径的 LCA 即为其两端点的 LCA

我们先将树以 \(1\) 为根固定下来。

对于求给定两个路径交的长度,在用 ST 表实现 \(O(n\log n)-O(1)\) LCA 的情况下可以 \(O(1)\) 求出。这里不再赘述。(分类讨论一下即可)

路径相交形成的新路径可以分成两种情况,一种是新路径的一个端点是另一个点的祖先;另一种是两个端点没有父子关系。

先考虑比较简单的情况,一个端点是另一个点的祖先的情况。上面那篇题解中给出的是用主席树来维护。实际上并不用,我们可以用 dp \(O(n)\) 处理出这种情况的答案。

我们设 \(mx_{i,0/1}\) ,分别表示编号为 \(mx_{i,0/1}\) 的路径的 LCA 是所有经过 \(i\) 路径中 LCA 深度最浅/次浅的。(\(mx_{i,0/1}\) 要不同)

只要知道这个,我们就可以对每个点的 \(mx_{i,0}\)\(mx_{i,1}\) 求一下他们两个的路径交长度,然后去个极值即可。

注意到如果一条路径经过 \(i\),其端点肯定在 \(i\) 的子树内,那么就是对子树求一个 min 。(即使直接求 min 求出来的路径不经过 i 也不影响答案的统计)

那么这部分我们可以 \(O(n)\) 解决。

然后对于第二种,我们注意到如果路径 \(i,j\) 形成了这种路径,那么路径 \(i,j\) 的 LCA 一定是相同的。

我们可以考虑通过一种类似差分的方式求出答案。

在遍历到路径的一个端点时加入另一个端点,并且通过启发式合并转移上来。

然后我们考虑合并时产生的贡献。

对于 \(l_i,l_j\) 设他们在 \(u\) 时被转移到一个集合里面。那么答案就是 \(dep_u+dep_{lca(r_i,r_j)}-dep_{lca(l_i,r_i)}\)

发现贡献之和 \(lca(r_i,r_j)\) 有关。

这里我们有个结论: 对于一个点和其他点求 LCA,dfs 序差小的一定比大的更优秀。

(这里的优秀指深度深,也就是对答案的贡献更优秀)

那么我们在插入 \(r_i\) (或 \(r_j\))的时候就找 \(dfs\) 序和它近的即可(前驱和后继)

那么可以用 set 来维护。

复杂度 \(O(n\log n\log n)\)

而 mzz 的题解中错了的原因是因为如果两条路径的 LCA 不同,那么最优的情况不一定是 dfs 序最接近的。在这个时候应该把经过每个点的所有路径的 LCA 的 \(dep\) 的最小值存下来,一起 DSU 才对。

hack 数据在 mzz 题解的评论区里了。

而我的做法中 LCA 不同的情况已经用 dp 处理了。

两种能过 hack 的做法的代码都贴一下:

我的

#include<bits/stdc++.h>
#define MIT multiset<node>::iterator
using namespace std;
const int MAXN = 2e5+5;
int n,K,st[21][MAXN],lg[MAXN],td,dfn[MAXN],ed[MAXN],dep[MAXN];
int U[MAXN],V[MAXN],ansl,ansr,mx[2][MAXN];
int Ans;
vector<int> e[MAXN];

struct node
{
	int p,id;
	bool operator < (const node&x)const
	{
		return dfn[p]!=dfn[x.p]?dfn[p]<dfn[x.p]:id<x.id;
	}
};
vector <node> Add[MAXN],Del[MAXN];
multiset<node> S[MAXN];

void dfs(int p,int fa)
{
	st[0][td]=fa;dfn[p]=++td;dep[p]=dep[fa]+1;
	for(int v:e[p]) if(v!=fa) dfs(v,p);
	ed[p]=td;
}
bool in(int u,int v){return dfn[u]<=dfn[v]&&ed[v]<=ed[u];}
int Min(int x,int y){return dfn[x]<=dfn[y]?x:y;}
int MIN(int x,int y){return dep[x]>=dep[y]?x:y;}
void init()
{
	for(int i=2;i<=td;++i) lg[i]=lg[i>>1]+1;
	for(int j=1;j<=lg[td];++j)
		for(int i=1;i+(1<<j)-1<td;++i)
			st[j][i]=Min(st[j-1][i],st[j-1][i+(1<<(j-1))]);
}
int Q(int x,int y)
{
	if(x==y) return x;
	x=dfn[x];y=dfn[y];if(x>y) swap(x,y);--y;
	int k=lg[y-x+1];
	return Min(st[k][x],st[k][y-(1<<k)+1]);
}
int dist(int x,int y){return dep[x]+dep[y]-dep[Q(x,y)]*2;}
void Upd_Ans(int x,int y)
{
	int u1=U[x],u2=U[y],v1=V[x],v2=V[y],lca=Q(u1,v1);
	if(x==y) return ;
	if(!in(Q(u2,v2),lca)) return ;
	if(!in(lca,u2)&&!in(lca,v2)) return ;
	int u=MIN(Q(u1,u2),Q(u1,v2)),v=MIN(Q(v1,v2),Q(v1,u2));
	if(dist(u,v)>Ans){Ans=dist(u,v);ansl=x;ansr=y;}
}
int Get_lca(int id){return id==0?0:Q(U[id],V[id]);}
void Upd(int p,int id)
{
	if(id==0||mx[0][p]==id||mx[1][p]==id) return ;
	int m0=mx[0][p],m1=mx[1][p];
	if(dep[Get_lca(m1)]>dep[Get_lca(id)]) m1=id;
	if(dep[Get_lca(m0)]>dep[Get_lca(m1)]) swap(m1,m0);
	mx[0][p]=m0;mx[1][p]=m1;
}

void Get_mx(int p,int fa)
{
	for(int v:e[p])
		if(v!=fa){Get_mx(v,p);Upd(p,mx[0][v]);Upd(p,mx[1][v]);}
	Upd_Ans(mx[0][p],mx[1][p]);Upd_Ans(mx[1][p],mx[0][p]);
}

void Insert(int p,node x)
{
	MIT it=S[p].lower_bound(x);
	if(it!=S[p].end())
		Upd_Ans(x.id,it->id),Upd_Ans(it->id,x.id);
	if(it!=S[p].begin())
		--it,Upd_Ans(x.id,it->id),Upd_Ans(x.id,it->id);
	S[p].insert(x);
}

void solve(int p,int fa)
{
	for(int v:e[p])
	{
		if(v!=fa)
		{
			solve(v,p);
			if(S[v].size()>S[p].size()) swap(S[p],S[v]);
			for(node i:S[v]) Insert(p,i);
		}
	}
	for(node i:Add[p]) Insert(p,i);
	for(node i:Del[p]) S[p].erase(S[p].find(i));
}


int main()
{
//	freopen("tree.in","r",stdin);
//	freopen("tree.out","w",stdout);
	scanf("%d %d",&n,&K);
	for(int i=2;i<=n;++i)
	{
		int fa;scanf("%d",&fa);
		e[fa].push_back(i);
		e[i].push_back(fa);
	}
	dfs(1,0);init();ansl=1;ansr=2;dep[0]=n+1;
	for(int i=1;i<=K;++i)
	{
		scanf("%d %d",&U[i],&V[i]);
		int lca=Q(U[i],V[i]);
		Add[U[i]].push_back(node{V[i],i});Add[V[i]].push_back(node{U[i],i});
		Del[lca].push_back(node{U[i],i});Del[lca].push_back(node{V[i],i});
		Upd(U[i],i);Upd(V[i],i);
	}
	Get_mx(1,0);
	solve(1,0);
	printf("%d\n%d %d\n",Ans,ansl,ansr);
	return 0;
}

mzz 过了hack 的:

#include<bits/stdc++.h>

using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

#define pb push_back
template <class T> inline void cmin(T &a,T b){ if(a>b) a=b; }
template <class T> inline void cmax(T &a,T b){ if(a<b) a=b; }

char IO;
template <class T=int> T rd(){
	T s=0;
	int f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}


const int N=2e5+10;

int n,m;
vector <int> G[N];
int a[N],b[N];

int line[N<<1],pos[N],lc,Log[N<<1],dep[N];
int s[20][N<<1];

void dfs(int u,int f) {
	line[pos[u]=++lc]=u;
	for(int v:G[u]) if(v!=f) {
		dep[v]=dep[u]+1,dfs(v,u);
		line[++lc]=u;
	}
}

void PreMake(){
	rep(i,2,lc) Log[i]=Log[i>>1]+1;
	rep(i,1,lc) s[0][i]=line[i];
	rep(i,1,Log[lc]) {
		int len=1<<(i-1);
		rep(j,1,lc+1-(1<<i)) {
			s[i][j]=dep[s[i-1][j]]<dep[s[i-1][j+len]]?s[i-1][j]:s[i-1][j+len];
		}
	}
}
int LCA(int x,int y) {
	x=pos[x],y=pos[y];
	if(x>y) swap(x,y);
	int d=Log[y-x+1];
	return dep[s[d][x]]<dep[s[d][y-(1<<d)+1]]?s[d][x]:s[d][y-(1<<d)+1];
}

struct Node{
	int x,y;
	bool operator < (const Node __) const {
		return x!=__.x?pos[x]<pos[__.x]:y<__.y;
	}
};

set <Node> S[N];
vector <Node> Add[N],Del[N],Sp[N];
int ans,ansx=1,ansy=2;

int Get(int a,int b,int c,int d) {
	return max(0,dep[LCA(b,d)]-max(dep[a],dep[c]));
}
void Upd_Ans(int i,int j) {
	if(i==j) return;
	int x=LCA(a[i],b[i]),y=LCA(a[j],b[j]);
	int t=Get(x,a[i],y,a[j])+Get(x,a[i],y,b[j])+Get(x,b[i],y,a[j])+Get(x,b[i],y,b[j]);
	if(t>ans) ans=t,ansx=i,ansy=j;
}

typedef set <Node> ::iterator iter;
void Insert(int u,Node x){
	iter it=S[u].lower_bound(x);
	if(it!=S[u].end()) Upd_Ans(x.y,it->y);
	if(it!=S[u].begin()) Upd_Ans(x.y,(--it)->y);
	S[u].insert(x);
}

void dfs_getans(int u,int f) {
	for(Node i:Sp[u]) Insert(u,i);
	for(int v:G[u]) if(v!=f) {
		dfs_getans(v,u);
		if(S[v].size()>S[u].size()) swap(S[u],S[v]);
		for(Node i:S[v]) Insert(u,i);
	}
	for(Node i:Add[u]) Insert(u,i);
	for(Node i:Del[u]) S[u].erase(i);
}

int main(){
	freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
	n=rd(),m=rd();
	rep(i,2,n) {
		int f=rd();
		G[i].pb(f),G[f].pb(i);
	}
	dfs(1,0),PreMake();
	rep(i,1,m) {
		a[i]=rd(),b[i]=rd();
		int lca=LCA(a[i],b[i]);
		Add[a[i]].pb((Node){b[i],i}),Add[b[i]].pb((Node){a[i],i});
		Sp[lca].pb((Node){b[i],i}),Sp[lca].pb((Node){a[i],i});
		Del[lca].pb((Node){b[i],i}),Del[lca].pb((Node){a[i],i});
	}
	dfs_getans(1,0);
	printf("%d\n%d %d\n",ans,ansx,ansy);
}

posted @ 2022-04-09 19:09  夜空之星  阅读(90)  评论(0编辑  收藏  举报