zjoi day2 語言

https://www.luogu.org/problemnew/show/P5327

最可做的題結果場上根本沒想打了60分,後來聽說想寫正解的都被卡了......做法:考慮對一個點x來說,可以到達它的點一定構成一個樹上包含x的連通塊,而連通塊裏距離x點最遠的點一定是跨過這個點的路徑的端點,發現這個連通塊的求法可以用類似虛樹的方法做,把所有跨過該點的路徑的端點按dfs序排序,先假設根會在連通塊裏,那麼發現按dfs序一個個加入點,假設上一次加了點p,這一次加入q,q產生的貢獻就是dep(q)-dep(lca(p,q))【可畫圖理解】,最後因爲根是假設在裏面的,連通塊的要減去dep(所有點lca)。知道對一個點求能到達的點的數量後,考慮如何對所有點統計。用線段樹維護按dfs序排序的點的選取情況,發現[l,mid]子樹和[mid+1,r]子樹合併時只要知道子樹構成連通塊dfs序最小最大的兩個點是什麼就可以按之前的方法O(1)合併了。而一段路徑能產生的貢獻可以用樹上差分來搞,假設一條u到v的路徑,可以在u,v處各加入u,v,在lca處統計完後減兩個u兩個v,那麼上線段樹合併即可。

代碼:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+100;

ll ans=0;
int n,m,dep[N],dfn[N],idx[N],tim;
vector<int>mp[N],add[N],del[N];

namespace LCA{
	int fi[N],ol[3*N],mn[3*N][21],wh[3*N][21],lg[3*N];
	void dfs(int x,int fa)
	{
		fi[x]=++tim,ol[tim]=x;
		for(int i=0,v;i<mp[x].size();i++)
		{
			v=mp[x][i];
			if(v==fa)continue;
			dep[v]=dep[x]+1,dfs(v,x),ol[++tim]=x;
		}
	}
	void init()
	{
		dep[1]=1,tim=0,dfs(1,0);
		lg[1]=0;
		for(int i=2;i<=tim;i++)lg[i]=lg[i>>1]+1;
		for(int i=1;i<=tim;i++)wh[i][0]=ol[i],mn[i][0]=dep[ol[i]];
		for(int i=1,len=2,md;i<=20;i++,len<<=1)
		{
			md=len>>1;
			for(int j=1;j+len-1<=tim;j++)
			{
				if(mn[j][i-1]<=mn[j+md][i-1])wh[j][i]=wh[j][i-1];
				else wh[j][i]=wh[j+md][i-1];
				mn[j][i]=dep[wh[j][i]];
			}
		}
	}
	int lca(int u,int v)
	{
		u=fi[u],v=fi[v];
		if(u>v)swap(u,v);
		int tmp=lg[v-u+1],len=1<<tmp;
		if(mn[u][tmp]<=mn[v-len+1][tmp])return wh[u][tmp];
		else return wh[v-len+1][tmp];	
	}
}

namespace SEG{
	struct gg{
		int sz,ld,rd,ls,rs,val;
	}seg[N*40];int snum,rt[N];
	gg mg_nd(gg x,gg y)
	{
		gg res;int tmp=LCA::lca(x.rd,y.ld);
		res.sz=x.sz+y.sz-dep[tmp];
		res.ld=x.ld,res.rd=y.rd;
		return res;
	}
	void push_up(int nw)
	{
		int ls=seg[nw].ls,rs=seg[nw].rs;
		seg[nw].sz=seg[nw].ld=seg[nw].rs=0;
		if(seg[ls].sz)seg[nw]=seg[ls];
		if(seg[rs].sz)
		{
			if(seg[nw].sz)seg[nw]=mg_nd(seg[nw],seg[rs]);
			else seg[nw]=seg[rs];
		}
		seg[nw].ls=ls,seg[nw].rs=rs;
	}
	void leaf(int x,int ps)
	{
		if(seg[x].val)seg[x].sz=dep[ps];
		else seg[x].sz=0;
		seg[x].ld=seg[x].rd=ps;
	}
	void cg(int &nw,int to,int l,int r,int w)
	{
		if(!nw)nw=++snum;
		if(l==r)seg[nw].val+=w,leaf(nw,idx[l]);
		else
		{
			int mid=(l+r)>>1;
			if(to<=mid)cg(seg[nw].ls,to,l,mid,w);
			else cg(seg[nw].rs,to,mid+1,r,w);
			push_up(nw);
		}
	}
	int mg_seg(int x,int y,int l,int r)
	{
		if(!x||!y)return x+y;
		if(l==r)
		{
			seg[x].val+=seg[y].val,leaf(x,idx[l]);
			return x;
		}
		int mid=(l+r)>>1;
		seg[x].ls=mg_seg(seg[x].ls,seg[y].ls,l,mid);
		seg[x].rs=mg_seg(seg[x].rs,seg[y].rs,mid+1,r);
		push_up(x);
		return x;
	}
	int ask(int x)
	{return seg[x].sz-dep[LCA::lca(seg[x].ld,seg[x].rd)];}
}
using namespace SEG;

void dfs0(int x,int fa)
{
	dfn[x]=++tim,idx[tim]=x;
	for(int v,i=0;i<mp[x].size();i++)
	{
		v=mp[x][i];
		if(v==fa)continue;
		dfs0(v,x);
	}
}

void dfs1(int x,int fa)
{
	for(int v,i=0;i<mp[x].size();i++)
	{
		v=mp[x][i];
		if(v==fa)continue;
		dfs1(v,x),rt[x]=mg_seg(rt[x],rt[v],1,tim);
	}
	for(int i=0;i<add[x].size();i++)
	{
		cg(rt[x],dfn[add[x][i]],1,tim,1);
	}
	ans+=max(0,ask(rt[x]));
	for(int i=0;i<del[x].size();i++)
		cg(rt[x],dfn[del[x][i]],1,tim,-2);
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int u,v,i=1;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		mp[u].push_back(v);
		mp[v].push_back(u);
	}
	LCA::init(),tim=0,dfs0(1,0);
	for(int i=1,u,v,lca;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		lca=LCA::lca(u,v);
		add[u].push_back(u),add[u].push_back(v);
		add[v].push_back(u),add[v].push_back(v);
		del[lca].push_back(u),del[lca].push_back(v);
	}
	dfs1(1,0);
	printf("%lld\n",ans/2);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章