P5327 - 语言 题解

时间:2021-09-20
本文章向大家介绍P5327 - 语言 题解,主要包括P5327 - 语言 题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

究极巨大神仙题。。。。(刚学线段树合并就做这么难的题啊?——小 c)

我们知道序列数点对一般离不开两种方法:cdq 分治和扫描线(就是对每个 \(r\) 数有多少个 \(l\))。对应到树上的话,cdq 分治就是点分治,扫描线变异出两种:枚举 lca 和枚举点对中的一个点。这题我们采用第三种方法。

\(x\) 可以开展贸易活动的点显然是所有经过 \(x\)\((u,v)\) 的并。我们考虑对每个点都维护一个数据结构维护 \((u,v)\) 们的并,这样对每个 \((u,v)\) 我们都要在所有 \(x\in(u,v)\) 的数据结构上做 \((u,v)\) 链加。考虑树上差分,这样问题转化为对该数据结构做一遍子树和。那么容易想到可以用线段树合并维护,此时由于有链加,要树剖,总复杂度 2log。而且线段树合并上还要打懒标记 / 标记永久化,比较麻烦。

考虑优化。容易发现,对每个 \(x\)​,包含 \(x\)​ 的 \((u,v)\)​ 的并是一个包含 \(x\)​ 的连通块。感性理解发现其实就是所有 \(u,v\)​ 的虚树。证明(考场上就不证明了吧,太显然了):考虑两个 \((u_1,v_1),(u_2,v_2)\)​,它们都包含 \(x\)​,必然相交。设交为 \((u_3,v_3)\)​,不妨设 \(u_1,u_2\)​ 靠近 \(u_3\)​,\(v_1,v_2\)​ 靠近 \(v_3\)​,那么显然 \((u_1,u_2)=(u_1,u_3)\cup(u_2,u_3)\subseteq(u_1,v_1)\cup(u_2,v_2)\)​,\((v_1,v_2),(u_1,v_2),(u_2,v_1)\)​ 同理。所以说所有 \((u,v)\)​ 的并其实就是所有 \(u,v\)​ 两两之间的路径并,那么显然就是所有 \(u,v\)​ 的虚树。

我们现在想知道虚树的大小。这是一个很经典的 trick(参考 P3320)——动态维护虚树大小。解决这个问题并不需要把虚树建出来、把边连起来,只要想象模拟 dfs 的过程,将点们按 dfn 排序得到 \(v\),那么所有 \(v_i\to v_{i\bmod |v|+1}\) 会恰好把虚树的每条边访问两遍。所以我们只需要求相邻点的距离和即可。

现在我们想知道 \(x\)​ 的若干子树内的 \(u,v\)​ 集合并起来之后(顺便加入 \(x\)​ 的 todo-list),相邻点距离和等于多少。考虑线段树合并,区间维护 dfn 落在该区间内的相邻点距离和,上传只需要再维护区间内最左和最右点。线段树合并只要考虑在一满一空时和两叶子是是否可行(其他时候上传),前者直接移植,后者直接搞。总复杂度 1log。注意叶子如果被累加多次要真的累加多次(反正不影响答案),因为有减法操作。

code 还挺好写的
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=200010,LOG_N=20;
int n,m;
vector<int> nei[N];
vector<int> add[N],del[N];
int fa[N];
int euler[N],fst[N],noweuler,dep[N],dfn[N],nowdfn,mng[N];
void dfs(int x=1){
	euler[++noweuler]=x;fst[x]=noweuler;
	dfn[x]=++nowdfn;mng[nowdfn]=x;
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa[x])continue;
		dep[y]=dep[x]+1;
		fa[y]=x;
		dfs(y);
		euler[++noweuler]=x;
	}
}
bool cmp(int x,int y){return dep[x]<dep[y];}
struct stable{
	int _log[N],mn[N][LOG_N];
	void init(){
		for(int i=2;i<=2*n;i++)_log[i]=_log[i-1]+(1<<_log[i-1]+1==i);
		for(int i=1;i<2*n;i++)mn[i][0]=euler[i];
		for(int j=1;j<LOG_N;j++)for(int i=1;i+(1<<j)-1<2*n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1],cmp);
	}
	int _mn(int l,int r){
		int log0=_log[r-l+1];
		return min(mn[l][log0],mn[r-(1<<log0)+1][log0],cmp);
	}
}st;
int lca(int x,int y){
	x=fst[x],y=fst[y];
	if(x>y)swap(x,y);
	return st._mn(x,y);
}
int dis(int x,int y){
	x=mng[x],y=mng[y];
	int a=lca(x,y);
	return dep[x]+dep[y]-2*dep[a];
}
struct segtree{
	int sz,root[N];
	struct node{int lson,rson,cnt,sum,lft,rit;}nd[N<<5];
	#define lson(p) nd[p].lson
	#define rson(p) nd[p].rson
	#define cnt(p) nd[p].cnt
	#define sum(p) nd[p].sum
	#define lft(p) nd[p].lft
	#define rit(p) nd[p].rit
	int nwnd(){return nd[++sz]=node({0,0,0,0,0,0}),sz;}
	void init(){
		sz=0,nd[0]=node({0,0,0,0,0,0});
		for(int i=1;i<=n;i++)root[i]=nwnd();
	}
	void sprup(int p){
		sum(p)=sum(lson(p))+sum(rson(p))+(rit(lson(p))&&lft(rson(p))?dis(rit(lson(p)),lft(rson(p))):0);
		lft(p)=lft(lft(lson(p))?lson(p):rson(p));rit(p)=rit(rit(rson(p))?rson(p):lson(p));
	}
	void add(int x,int v,int p,int tl=1,int tr=n){
		if(tl==tr)return cnt(p)+=v,lft(p)=rit(p)=cnt(p)?tl:0,void();
		int mid=tl+tr>>1;
		if(x<=mid){
			if(!lson(p))lson(p)=nwnd();
			add(x,v,lson(p),tl,mid);
		}
		else{
			if(!rson(p))rson(p)=nwnd();
			add(x,v,rson(p),mid+1,tr);
		}
		sprup(p);
	}
	int mrg(int p,int q,int tl=1,int tr=n){
		if(!p||!q)return p|q;
		if(tl==tr)return cnt(p)+=cnt(q),lft(p)=rit(p)=cnt(p)?tl:0,p;
		int mid=tl+tr>>1;
		lson(p)=mrg(lson(p),lson(q),tl,mid),rson(p)=mrg(rson(p),rson(q),mid+1,tr);
		return sprup(p),p;
	}
}segt;
long long ans;
void dfs0(int x=1){
	for(int i=0;i<add[x].size();i++)segt.add(dfn[add[x][i]],1,segt.root[x]);
	for(int i=0;i<del[x].size();i++)segt.add(dfn[del[x][i]],-1,segt.root[x]);
	for(int i=0;i<nei[x].size();i++){
		int y=nei[x][i];
		if(y==fa[x])continue;
		dfs0(y);
		segt.root[x]=segt.mrg(segt.root[x],segt.root[y]);
	}
	int p=segt.root[x];
	ans+=(segt.sum(p)+(segt.lft(p)?dis(segt.lft(p),segt.rit(p)):0))/2;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		nei[x].pb(y),nei[y].pb(x);
	}
	dep[1]=1;dfs();
	st.init();
	while(m--){
		int x,y;
		scanf("%d%d",&x,&y);
		int a=lca(x,y);
		add[x].pb(x),add[x].pb(y);
		add[y].pb(x),add[y].pb(y);
		del[a].pb(x),del[a].pb(y);
		del[fa[a]].pb(x),del[fa[a]].pb(y);
	}
	segt.init();
	dfs0();
	cout<<ans/2;
	return 0;
}
珍爱生命,远离抄袭!

原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-p5327.html