1. 程式人生 > 其它 >學習筆記:點分治

學習筆記:點分治

【題目】

  • P3806

  • 看了上面的題目,想必有很多種演算法解決。

  • \(1\)、列舉不同的兩個點,然後\(dfs\)算出之間的距離,判斷一下就行了。時間複雜度大概是\(O(n^3)\)

  • \(2\)、選一個節點做樹的根,求出每個點的深度。然後列舉兩個點,求\(lca\),簡單加減一下就行了。時間複雜度大概是\(O(n^2logn)\)

  • \(3\)、時間複雜度為\(O(nlogn)\)的演算法點分治。


【點分治演算法】

  • 點分治要我們每次選擇一個點\(A\),處理有關點\(A\)的路徑,然後將點\(A\)刪除,然後重複以上過程。

第一步:找重心

  • 選擇哪個點成為點\(A\)
    也是有學問的,選的不同會影響遍歷的效率。
  • 顯然選\(y\)比選\(x\)不優,選\(x\)最多遞迴\(2\)層,選\(y\)最多遞迴\(4\)層。

  • 所以,點\(A\)應該是當前子樹的重心(重心所有的子樹大小的最大值小於整個樹大小的一半)。

第二步:求距離

  • 有關點\(A\)的路徑無非就兩種情況。

    • \(A\)為鏈首,點\(A\)的子樹上另外一點為鏈尾
    • \(A\)其中一個子樹上的點為鏈首,點\(A\)另外一棵子樹上的點為鏈尾,繞過點\(A\)
  • 我們對重心的子樹逐一處理\(\Longrightarrow\)就是先處理完一個子樹,再處理另一個子樹。

  • 先算出當前子樹第\(i\)

    號節點到點\(A\)的距離\(d_i\),然後我們在桶裡面查詢\(k-d_i\),若找到了,便結束了。

  • 當前子樹查詢完了以後,便將\(d_i\)丟進桶裡。

  • 切忌一邊查詢一邊將\(d_i\)丟進桶裡

  • 處理完所有子樹,便在桶裡查詢\(k\),代表尋找子樹中是否有一點與點\(A\)距離為\(k\)

第三步:遞迴

  • \(A\)的子樹進行上述操作。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int INF=2147483647;
const int N=10100;
int n,m,b[N];
bool ans[N];
struct EDGE 
{ 
  int v,w,nx; 
}lb[N<<1]; 
int tot=1,top[N];
void add(int u,int v,int w)
{ 
  lb[++tot]=(EDGE){v,w,top[u]};
  top[u]=tot; 
}
bool vis[N];
int siz[N],masiz[N],q[N],he,ta;
void dfs0(int u)
{
	q[++ta]=u,vis[u]=1;
	siz[u]=1,masiz[u]=0;
	for(int i=top[u]; i; i=lb[i].nx)
	{
		int v=lb[i].v;
		if(vis[v]) continue;
		dfs0(v);
		siz[u]+=siz[v];
		masiz[u]=max(masiz[u],siz[v]);
	}
	vis[u]=0;
}
int d[N];
void dfs1(int u)
{
	q[++ta]=u,vis[u]=1;
	for(int i=top[u]; i; i=lb[i].nx)
	{
		int v=lb[i].v,w=lb[i].w;
		if(vis[v]) continue;
		d[v]=d[u]+w;
		dfs1(v);
	}
	vis[u]=0;
}
bool t[10000010];
void calc(int l,int r)
{
	for(int i=l; i<=r; i++)
	{
		int u=q[i];
		for(int j=1; j<=m; j++) 
		  if(b[j]>=d[u])ans[j]|=t[b[j]-d[u]];
	}
	for(int i=l; i<=r; i++) 
	  if(d[q[i]]<=1e7)t[d[q[i]]]=1;
}
void clear()
{
	for(int i=1; i<=ta; i++) 
	  if(d[q[i]]<=1e7)t[d[q[i]]]=0;
}
void solve(int u)
{
	he=1,ta=0,dfs0(u);
	int mi=INF,s=siz[u];
	for(int i=1; i<=ta; i++)
	{
		int v=q[i],g=max(masiz[v],s-siz[v]);
		if(g<mi) mi=g,u=v;
	}
	he=1,ta=0;
	d[u]=0,vis[u]=1;
	for(int i=top[u]; i; i=lb[i].nx)
	{
		int v=lb[i].v,w=lb[i].w;
		if(vis[v])continue;
		d[v]=d[u]+w;
		dfs1(v);
		calc(he,ta);
		he=ta+1;
	}
	q[++ta]=u;
	calc(he,ta);
	clear();
	for(int i=top[u]; i; i=lb[i].nx)
	{
		int v=lb[i].v;
		if(vis[v]) continue;
		solve(v);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1; i< n; i++)
	{
		int x,y,w; 
		scanf("%d%d%d",&x,&y,&w);
		add(x,y,w),add(y,x,w);
	}
	for(int i=1; i<=m; i++)scanf("%d",&b[i]);
	solve(1);
	for(int i=1; i<=m; i++)
	  if(ans[i])printf("AYE\n");
	  else printf("NAY\n");
    return 0;
}