【BZOJ3653】談笑風生(長鏈剖分)


【BZOJ3653】談笑風生(長鏈剖分)

題面

BZOJ
洛谷
權限題啊。。。。

題解

首先根據題目給的條件,發現\(a,b\)都要是\(c\)的父親。
所以這三個點是樹上的一條深度單增的鏈。
因為\(a,b\)之間距離不超過\(k\),並且\(a\)被欽定了,所以只有兩種情況:
一種是\(a\)\(b\)的祖先,貢獻是\(\sum_b size[b]-1\),也就是所有\(b\)可以選擇的點的子樹和。
另外一種\(b\)\(a\)的祖先,貢獻是\(\sum_b size[a]-1\),欽定一個\(b\)之后,\(c\)可以在\(a\)的子樹中任選。
第二種情況很簡單,因為\(size[a]\)是定值,並且每個點的父親是唯一的,所以第二部分很容易算。
困難的是第一部分,然而依舊不難把。。。
方法很多,比如說,你把\(dfs\)序和深度看成\(x,y\)軸,這樣子就是二維數點,直接主席樹。
或者說直接點分治也可以。

當然,既然想寫長鏈剖分,那就當然要用長鏈剖分來做啊。
我們發現,所有的值都由重兒子向后挪動一位得來,而我們要求的東西需要維護一個區間和。
這樣子很不好用前綴和來做,所以我們可以用一個后綴和啊!
這樣子就很舒服了,直接維護后綴和,然后長鏈剖分轉移,可以做到復雜度\(O(n)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 300300
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Ask{int id,k;};
vector<Ask> p[MAX];
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
ll tmp[MAX],*s[MAX],*id=tmp,ans[MAX];
int md[MAX],dep[MAX],fa[MAX],hson[MAX],size[MAX];
void dfs1(int u,int ff)
{
	md[u]=dep[u]=dep[ff]+1;fa[u]=ff;size[u]=1;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==ff)continue;
		dfs1(v,u);size[u]+=size[v];
		if(md[v]>md[hson[u]])hson[u]=v;
	}
	if(hson[u])md[u]=md[hson[u]];
}
void dfs(int u)
{
	s[u][0]=size[u]-1;
	if(hson[u])s[hson[u]]=s[u]+1,dfs(hson[u]),s[u][0]+=s[hson[u]][0];
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==fa[u]||v==hson[u])continue;
		s[v]=id;id+=md[v]-dep[v]+1;dfs(v);
		for(int j=0;j<=md[v]-dep[v];++j)s[u][j+1]+=s[v][j];
		s[u][0]+=s[v][0];
	}
	for(int i=p[u].size()-1;~i;--i)
	{
		int k=p[u][i].k,id=p[u][i].id;
		ans[id]+=1ll*(size[u]-1)*min(dep[u]-1,k);
		if(k>=md[u]-dep[u])ans[id]+=s[u][0]-size[u]+1;
		else ans[id]+=s[u][0]-size[u]+1-s[u][k+1];
	}
}
int n,Q;
int main()
{
	n=read();Q=read();
	for(int i=1;i<n;++i)
	{
		int u=read(),v=read();
		Add(u,v);Add(v,u);
	}
	dfs1(1,0);
	for(int i=1;i<=Q;++i)
	{
		int u=read(),k=read();
		p[u].push_back((Ask){i,k});
	}
	s[1]=id;id+=md[1];dfs(1);
	for(int i=1;i<=Q;++i)printf("%lld\n",ans[i]);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM