[ZJOI2019]語言

題面

題意

有一棵有n個點的樹,上面有m條鏈,兩個點可以互達當且僅當存在一條兩點都在上面的鏈.
問有幾對點可以互達.

做法

對每個點考慮它對答案的貢獻,可以發現,若點x在鏈a1,b1;a2,b2.....a_1,b_1;a_2,b_2.....上,則與x可以互達的點恰好都在點a1,b1,a2,b2.....a_1,b_1,a_2,b_2.....構成的虛樹上(包括被壓縮在邊上的點),因此我們可以直接將所有點根據dfs序排序,然後用線段樹來儲存,線段樹中的每個葉子節點表示:該點是否是包含點x的鏈上的某個端點,然後在up時即可統計答案:左右兩邊的答案之和,加上兩個聯通塊之間的點數再,減去左邊dfs序最大點與右邊dfs序最小的點的lca與x的深度差(去重).
然後用線段樹合併即可處理,再用st表來實現O(1)O(1)求lca,這樣總的時間複雜度即爲O(nlog2n)O(n*log_2n)

代碼

#include<bits/stdc++.h>
#define ll long long
#define LG 17
#define N 100100
using namespace std;

int n,m,tt,deep[N],dfn[N],in[N];
vector<int>to[N];

namespace LCA
{
	int tmp,pos[N],lg[N<<1],ou[N<<1],mn[N<<1][20];
	void dfs(int now,int last)
	{
		int i,t;
		ou[++tmp]=now;
		dfn[++tt]=now;
		pos[now]=tmp;
		in[now]=tt;
		for(i=0;i<to[now].size();i++)
		{
			t=to[now][i];
			if(t==last) continue;
			deep[t]=deep[now]+1;
			dfs(t,now);
			ou[++tmp]=now;
		}
	}
	inline void pre()
	{
		int i,j;
		deep[1]=1;
		dfs(1,-1);
		for(i=1;i<=tmp;i++) mn[i][0]=ou[i];
		for(j=1;j<=LG;j++)
		{
			for(i=1;i+(1 << (j-1))<=tmp;i++)
			{
				if(deep[mn[i][j-1]]<deep[mn[i+(1 << (j-1))][j-1]]) mn[i][j]=mn[i][j-1];
				else mn[i][j]=mn[i+(1 << (j-1))][j-1];
			}
		}
		for(i=2;i<=tmp;i++) lg[i]=lg[i>>1]+1;
	}
	inline int ask(int u,int v)
	{
		if(!u || !v) return u+v;
		u=pos[u],v=pos[v];
		if(u>v) swap(u,v);
		int l=lg[v-u+1];
		return deep[mn[u][l]]<deep[mn[v-(1 << l)+1][l]]?mn[u][l]:mn[v-(1 << l)+1][l];
	}
}

int rt[N];
ll ans;
struct Node
{
	int ls,rs,dn,sum,ld,rd,cnt;
}node[N*40];
vector<int>ad[N],del[N];

inline void up(int now)
{
	int L=node[now].ls,R=node[now].rs;
	if(!node[L].cnt)
	{
		node[now].dn=node[R].dn;
		node[now].sum=node[R].sum;
		node[now].ld=node[R].ld;
		node[now].rd=node[R].rd;
		node[now].cnt=node[R].cnt;
		return;
	}
	if(!node[R].cnt)
	{
		node[now].dn=node[L].dn;
		node[now].sum=node[L].sum;
		node[now].ld=node[L].ld;
		node[now].rd=node[L].rd;
		node[now].cnt=node[L].cnt;
		return;
	}
	node[now].dn=LCA::ask(node[L].dn,node[R].dn);
	node[now].sum=node[L].sum+node[R].sum+deep[node[L].dn]+deep[node[R].dn]-2*deep[node[now].dn]-1-(deep[LCA::ask(node[L].rd,node[R].ld)]-deep[node[now].dn]);
	node[now].ld=node[L].ld;
	node[now].rd=node[R].rd;
	node[now].cnt=node[L].cnt+node[R].cnt;
}

int mg(int u,int v,int l,int r)
{
	if(!u || !v) return u+v;
	if(l==r)
	{
		node[u].cnt+=node[v].cnt;
		return u;
	}
	int mid=((l+r)>>1);
	node[u].ls=mg(node[u].ls,node[v].ls,l,mid);
	node[u].rs=mg(node[u].rs,node[v].rs,mid+1,r);
	up(u);
	return u;
}

void add(int now,int l,int r,int u,int v)
{
	if(l==r)
	{
		node[now].cnt+=v;
		if(!node[now].cnt) return;
		node[now].dn=node[now].ld=node[now].rd=dfn[l];
		node[now].sum=1;
		return;
	}
	int mid=((l+r)>>1);
	if(u<=mid)
	{
		if(!node[now].ls) node[now].ls=++tt;
		add(node[now].ls,l,mid,u,v);
	}
	else
	{
		if(!node[now].rs) node[now].rs=++tt;
		add(node[now].rs,mid+1,r,u,v);
	}
	up(now);
}

void dfs(ll now,ll last)
{
	ll i,t;
	for(i=0;i<to[now].size();i++)
	{
		t=to[now][i];
		if(t==last) continue;
		dfs(t,now);
		rt[now]=mg(rt[now],rt[t],1,n);
	}
	for(i=0;i<ad[now].size();i++)
	{
		t=ad[now][i];
		add(rt[now],1,n,t,1);
	}
	if(node[rt[now]].cnt) ans+=node[rt[now]].sum-1;
	for(i=0;i<del[now].size();i++)
	{
		t=del[now][i];
		add(rt[now],1,n,t,-2);
	}
}

int main()
{
	int i,j,p,q,t;
	cin>>n>>m;
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&p,&q);
		to[p].push_back(q);
		to[q].push_back(p);
	}
	LCA::pre();
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&p,&q);
		t=LCA::ask(p,q);
		ad[p].push_back(in[p]),ad[p].push_back(in[q]);
		ad[q].push_back(in[p]),ad[q].push_back(in[q]);
		del[t].push_back(in[p]),del[t].push_back(in[q]);
	}
	for(i=1;i<=n;i++) rt[i]=i;
	tt=n;
	dfs(1,-1);
	cout<<ans/2;
}

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