1. 程式人生 > 其它 >LOJ 3066 - 「ROI 2016 Day2」快遞(線段樹合併+set 啟發式合併)

LOJ 3066 - 「ROI 2016 Day2」快遞(線段樹合併+set 啟發式合併)

set 啟發式合併+線段樹合併,毒瘤題

LOJ 題面傳送門

人傻常數大,需要狠命卡……/wq/wq

畫個圖可以發現兩條路徑相交無非以下兩種情況(其中紅色部分為兩路徑的重疊部分,粉色、綠色的部分分別表示兩條路徑):

考慮如何計算它們的貢獻,對於第一種情況,我們列舉兩條路徑 LCA 中深度較大者,也就是上圖中的 A 點。列舉這個點 \(x\) 以後進而列舉對應的路徑,設其為 \(u\to x\to v\),那麼我們考慮二分重疊部分的長度 \(len\),那麼這個重疊部分要麼是 \(u\)\(dep_u-dep_x-len\) 級祖先 \(fu\)\(x\) 的路徑,要麼是 \(v\)\(dep_v-dep_x-len\)

級祖先 \(fv\)\(x\) 的路徑,因此我們需要檢驗是否存在一條路徑經過 \(fu\to x\) 上所有點或者經過 \(fv\to x\) 上所有點。我們考慮用線段樹,維護一個端點在 \(x\) 子樹內,一個端點不在 \(x\) 子樹內的路徑。具體來說我們求出每個點的 DFS 序 \(dfn_x\),對於線段樹上下標為 \(p\) 的位置,我們存有多少條路徑 \((u,v)\),滿足 \(dfn_u=x\),且 \(u\)\(x\) 子樹內,\(v\)\(x\) 子樹外,這樣檢驗時只需做一遍區間求和。那麼怎麼維護這個線段樹呢?我們考慮對於每條路徑,在 \(u\) 處的線段樹上下標為 \(dfn_u\)
的位置上加一,\(v\) 處的線段樹上下標為 \(dfn_v\) 的位置上加一,\(\text{LCA}(u,v)\) 處線段樹上 \(dfn_u,dfn_v\) 位置上各減一,然後線段樹合併即可。

對於第二種情況,我們考慮在兩條路徑交界處(也就是上圖中粉色、綠色線段的上端點,紅色線段的下段點)處統計貢獻,我們還是對整棵樹進行一遍 DFS 並列舉對應的點 \(x\),那麼可以發現,如果我們把一個端點在 \(x\) 子樹內,一個端點在子樹外的路徑拎出來並按照它們在子樹外的端點的 DFS 序從小到大排序,最優的一對路徑在排好序的序列中肯定是相鄰的,因此我們考慮將所有路徑壓入一個 set,每次加入一棵子樹時啟發式合併,將大小小的 set

中元素暴力插入大小大的 set 中,插入時在 setlower_bound 中找到 DFS 序與其相鄰的點更新答案即可。

時間複雜度 \(n\log^2n\)

const int MAXN=2e5;
const int LOG_N=19;
int n,k,hd[MAXN+5],nxt[MAXN+5],to[MAXN+5],ec=0,cc[MAXN+5];
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dfn[MAXN+5],tim,edt[MAXN+5];
int dep[MAXN+5],top[MAXN+5],siz[MAXN+5],wson[MAXN+5];
int fa[LOG_N+2][MAXN+5];
void dfs01(int x){
	siz[x]=1;dfn[x]=++tim;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];dep[y]=dep[x]+1;dfs01(y);siz[x]+=siz[y];
		if(siz[y]>siz[wson[x]]) wson[x]=y;
	} edt[x]=tim;
}
void dfs02(int x,int tp){
	top[x]=tp;if(wson[x]) dfs02(wson[x],tp);
	for(int e=hd[x];e;e=nxt[e]) if(to[e]^wson[x]) dfs02(to[e],to[e]);
}
int getlca(int x,int y){
	while(top[x]^top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[0][top[x]];
	} return (dep[x]<dep[y])?x:y;
}
struct dat{int a,b,lc,dis;} c[MAXN+5];
int get_kanc(int x,int k){
	for(int i=LOG_N;~i;i--) if(k>>i&1) x=fa[i][x];
	return x;
}
namespace segtree{
	const int MAXP=MAXN*40;
	struct node{int ch[2],val;} s[MAXP+5];
	int rt[MAXN+5],ncnt=0;
	void pushup(int k){s[k].val=s[s[k].ch[0]].val+s[s[k].ch[1]].val;}
	void insert(int &k,int l,int r,int p,int v){
		if(!k) k=++ncnt;if(l==r) return s[k].val+=v,void();
		int mid=l+r>>1;
		if(p<=mid) insert(s[k].ch[0],l,mid,p,v);
		else insert(s[k].ch[1],mid+1,r,p,v);
		pushup(k);
	}
	int query(int k,int l,int r,int ql,int qr){
		if(!k) return 0;
		if(ql<=l&&r<=qr) return s[k].val;
		int mid=l+r>>1;
		if(qr<=mid) return query(s[k].ch[0],l,mid,ql,qr);
		else if(ql>mid) return query(s[k].ch[1],mid+1,r,ql,qr);
		else return query(s[k].ch[0],l,mid,ql,mid)+query(s[k].ch[1],mid+1,r,mid+1,qr);
	}
	int merge(int x,int y,int l,int r){
		if(!x||!y) return x+y;int z=++ncnt,mid=l+r>>1;
		if(l==r) return s[z].val=s[x].val+s[y].val,z;
		s[z].ch[0]=merge(s[x].ch[0],s[y].ch[0],l,mid);
		s[z].ch[1]=merge(s[x].ch[1],s[y].ch[1],mid+1,r);
		pushup(z);return z;
	}
}
using segtree::rt;
using segtree::insert;
using segtree::query;
using segtree::merge;
vector<int> pth[MAXN+5];
vector<pii> add[MAXN+5],del[MAXN+5];
pair<int,int> mx_id1=mp(0,1);
pair<int,pii> mx_id2;
void dfs1(int x){
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];dfs1(y);
		rt[x]=merge(rt[x],rt[y],1,n);
	}
	insert(rt[x],1,n,dfn[x],cc[x]);
	for(int id:pth[x]){
		insert(rt[x],1,n,dfn[c[id].a],-1);
		insert(rt[x],1,n,dfn[c[id].b],-1);
		if(c[id].dis<mx_id1.fi) continue;
		if(c[id].a!=x){
			int len=dep[c[id].a]-dep[x];
			int l=1,r=dep[c[id].a]-dep[x],p=0;
			while(l<=r){
				int mid=l+r>>1,anc=get_kanc(c[id].a,len-mid);
				if(query(rt[x],1,n,dfn[anc],edt[anc])) p=mid,l=mid+1;
				else r=mid-1;
			} chkmax(mx_id1,mp(p,id));
		} if(c[id].b!=x){
			int len=dep[c[id].b]-dep[x];
			int l=1,r=dep[c[id].b]-dep[x],p=0;
			while(l<=r){
				int mid=l+r>>1,anc=get_kanc(c[id].b,len-mid);
				if(query(rt[x],1,n,dfn[anc],edt[anc])) p=mid,l=mid+1;
				else r=mid-1;
			} chkmax(mx_id1,mp(p,id));
		}
	}
}
bool has_anc(int x,int y){//whether x and y are ancestor and son
	return (getlca(x,y)==x)||(getlca(x,y)==y);
}
int calc_fa_son(int x1,int y1,int x2,int y2){//y1 is the ancestor of x1
	int lc=getlca(x1,x2);
	return max(dep[lc]-max(dep[y1],dep[y2]),0);
}
int calc(int x,int y){
	if(x==y) return -1;
	if(c[x].dis<mx_id2.fi) return 0;
	if(c[y].dis<mx_id2.fi) return 0;
	if(!has_anc(c[x].lc,c[y].lc)) return 0;
	return calc_fa_son(c[x].a,c[x].lc,c[y].a,c[y].lc)+
		   calc_fa_son(c[x].b,c[x].lc,c[y].a,c[y].lc)+
		   calc_fa_son(c[x].a,c[x].lc,c[y].b,c[y].lc)+
		   calc_fa_son(c[x].b,c[x].lc,c[y].b,c[y].lc);
}
set<pii> stv[MAXN+5];
bool ban[MAXN+5];
void upd_ans(int x,int y){chkmax(mx_id2,mp(calc(x,y),mp(x,y)));}
void ins_st(set<pii> &st,pii p){
	if(c[p.se].dis<mx_id2.fi) return ban[p.se]=1,void();
	else{
		set<pii>::iterator it=st.upper_bound(p);
		if(it!=st.end()) upd_ans(p.se,it->se);
		if(it!=st.begin()) upd_ans(p.se,(--it)->se);
		st.insert(p);
	}
}
void dfs2(int x){
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];dfs2(y);
		if(stv[y].size()>stv[x].size()) stv[x].swap(stv[y]);
		for(pii p:stv[y]) ins_st(stv[x],p);
	}
	for(pii p:add[x]) ins_st(stv[x],mp(dfn[p.fi],p.se));
	for(pii p:del[x]) if(!ban[p.se])
		stv[x].erase(stv[x].find(mp(dfn[p.fi],p.se)));
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d%d",&n,&k);
	for(int i=2,f;i<=n;i++) scanf("%d",&f),adde(f,i),fa[0][i]=f;
	dfs01(1);dfs02(1,1);
	for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++) fa[i][j]=fa[i-1][fa[i-1][j]];
	for(int i=1;i<=k;i++){
		scanf("%d%d",&c[i].a,&c[i].b);
		c[i].lc=getlca(c[i].a,c[i].b);
		if(c[i].lc!=c[i].a&&c[i].lc!=c[i].b) pth[c[i].lc].pb(i);
		cc[c[i].a]++;cc[c[i].b]++;
		add[c[i].a].pb(mp(c[i].b,i));
		add[c[i].b].pb(mp(c[i].a,i));
		del[c[i].lc].pb(mp(c[i].a,i));
		del[c[i].lc].pb(mp(c[i].b,i));
		c[i].dis=dep[c[i].a]+dep[c[i].b]-(dep[c[i].lc]<<1);
	} dfs1(1);
	for(int i=1;i<=k;i++) if(i^mx_id1.se)
		chkmax(mx_id2,mp(calc(i,mx_id1.se),mp(i,mx_id1.se)));
	dfs2(1);
	printf("%d\n%d %d\n",mx_id2.fi,mx_id2.se.fi,mx_id2.se.se);
	return 0;
}
/*
7 2
1 1 2 2 3 3
4 7
5 6
*/