給你一棵樹,路徑有長度,多次詢問,每次給出 k,問你是否存在路徑長度為 k 的點對。
這道題我們用分治的方法。
那我們假設要解決乙個樹,那我們先選重心作為根節點(為了減少高度節省時間)。
然後就分成兩種討論,一種是路徑都在同乙個子樹中,那這個我們就可以把問題轉換為解決這個子樹。
另一種就是在兩個不同的子樹中(或者乙個是根節點)。那我們考慮直接搜出點到根節點的距離,然後任意兩個相加都是一種可行的 \(k\)。(因為都是在不同子樹,或者是乙個是根節點的話距離就是另乙個到根節點的距離,所以是可以直接相加的)
首先,它可能是根節點,那它到根節點的距離就是 \(0\),那乙個的距離為 \(0\) 就是存在的。
然後你考慮枚舉子樹,然後求出每個點到根節點的距離。然後我們這樣弄:我們列舉每個點和你給出的詢問(也就是離線弄)的 \(y\),那首先,這個點到根節點的距離 \(x\) 一定是要小於等於 \(y\) 的,然後如果要使得這個詢問的結果是有,那距離為 \(y-x\) 的點是一定要存在的,因為這樣就可以通過這兩個,得出 \(y\) 的距離。
那你為了不會找到的點跟它是同乙個子樹中的,你就可以進行完上面的操作,才把你這個子樹的距離都標記為存在。
然後你每次不同的根統計那個存在的距離都是不同的,那你就要把之前的清空。
但是不能用 memset,因為陣列太大會超時。
但其實它真正有值的不多,那我們可以每次標記的時候把標記的點用乙個佇列記錄下來(棧也行,反正就是拿個陣列記錄值為 \(1\) 的位置),然後你要清空的時候就列舉這些值為 \(1\) 的地方,把它們賦值為 \(0\) 即可。
然後加個快讀什麼的就可以了。
#include#include#include#include#includeusing namespace std;
struct node e[200001];
struct tree t[100001];
int n, m, x, y, z, le[100001], kk;
bool in[100001], answer[101];
int ans[10000001], rem[101], que[10000001];
int d[100001], dis[100001], root;
int k, fa[100001], s, maxn[100001];
int read()
while (c >= '0' && c <= '9')
return re;
}bool cmp(int x, int y)
void add(int x, int y, int z) ; le[x] = kk;
}void find_root(int now, int father)
maxn[now] = max(maxn[now], s - t[now].s);
if (maxn[now] < maxn[root])
}void get_dis(int now, int father)
}void count_(int now)
} while (que[0])
}void dfs(int now)
}int main()
for (int i = 1; i <= m; i++)
s = n;
maxn[0] = n;
root = 0;
find_root(1, 0);
dfs(root);
for (int i = 1; i <= m; i++)
return 0;
}
luogu P3806 模板 點分治1
題目描述 給定一棵有n個點的樹 詢問樹上距離為k的點對是否存在。多次詢問 可離線 我們先隨意指定乙個虛擬根root,將這棵樹轉化成無根樹 樹上的路徑可以分為兩類,1.經過根節點u的路徑 2.完全在u子樹裡 不經過u 的 對於1,用dis表示當前結點到根節點root的路徑長度,則root的子樹中兩個節...
Luogu P3806 模板 點分治1
給定一棵有 n nn 個點,邊權的樹,回答 m mm 個詢問,每次詢問樹上距離為 k kk 的點對是否存在。資料範圍n 1 04,m 100,邊權 10000,k 107 n leqslant 10 4,m leqslant 100,texttt leqslant 10000,k leqslant ...
luogu P3806 模板 點分治1
給你一棵樹,路徑有長度,多次詢問,每次給出 k,問你是否存在路徑長度為 k 的點對。這道題我們用分治的方法。那我們假設要解決乙個樹,那我們先選重心作為根節點 為了減少高度節省時間 然後就分成兩種討論,一種是路徑都在同乙個子樹中,那這個我們就可以把問題轉換為解決這個子樹。另一種就是在兩個不同的子樹中 ...