1. 程式人生 > >[Luogu] P3806 【模板】點分治1

[Luogu] P3806 【模板】點分治1

oid sca stream 存在 pac main 長度 code namespace

題目背景

感謝hzwer的點分治互測。

題目描述

給定一棵有n個點的樹

詢問樹上距離為k的點對是否存在。

輸入輸出格式

輸入格式:

n,m 接下來n-1條邊a,b,c描述a到b有一條長度為c的路徑

接下來m行每行詢問一個K

輸出格式:

對於每個K每行輸出一個答案,存在輸出“AYE”,否則輸出”NAY”(不包含引號)

輸入輸出樣例

輸入樣例#1: 2 1 1 2 2 2 輸出樣例#1: AYE

說明

對於30%的數據n<=100

對於60%的數據n<=1000,m<=50

對於100%的數據n<=10000,m<=100,c<=1000,K<=10000000

題目解析

很有必要好好講講點分治,很有意思的算法。

對於一些樹上的詢問(如本題的路徑長度),直接進行計算可能帶來巨大的復雜度。

如果了解分治的話應該知道我們可以利用分治來降低一些東西的復雜度。

說白了,樹上分治分為點分治和邊分治。點分治就是一種樹上的分治算法。

就這道題而言,選取一個點為重心,所有路徑只有兩種情況:直接經過這個點或在這個點的子樹中。這時我們處理直接經過該點的路徑,同時進行分治,遞歸處理子樹。

但是有一個問題:這樣的算法可能被某些極端圖(鏈,菊花圖等)卡到時間爆炸,我們必須采取對策——找子樹的重心作為新的選取點,這樣就可以了

Code

#include<iostream>
#include
<cstdio> #include<algorithm> using namespace std; const int INF = 0x3f3f3f3f; const int MAXK = 10000000 + 5; const int MAXM = 100 + 5; const int MAXN = 10000 + 5; int n,m; int sum,root; struct Edge { int nxt; int to,w; } l[MAXN << 1]; int cnt,head[MAXN]; int maxpart[MAXN],siz[MAXN];
int dis[MAXN],rem[MAXN],q[MAXN]; int test[MAXK],judge[MAXK]; int query[MAXM]; bool vis[MAXN]; void add(int x,int y,int z) { cnt++; l[cnt].nxt = head[x]; l[cnt].to = y; l[cnt].w = z; head[x] = cnt; return; } void findroot(int x,int from) { siz[x] = 1; for(int i = head[x]; i; i = l[i].nxt) { if(l[i].to == from || vis[l[i].to]) continue; findroot(l[i].to,x); siz[x] += siz[l[i].to]; maxpart[x] = max(maxpart[x],siz[l[i].to]); } maxpart[x] = max(maxpart[x],sum - siz[x]); if(maxpart[x] < maxpart[root]) root = x; return; } void getdis(int x,int from) { rem[++rem[0]] = dis[x]; for(int i = head[x]; i; i = l[i].nxt) { if(l[i].to == from || vis[l[i].to]) continue; dis[l[i].to] = dis[x] + l[i].w; getdis(l[i].to,x); } return; } void calc(int x) { int tmp = 0; for(int i = head[x]; i; i = l[i].nxt) { if(vis[l[i].to]) continue; rem[0] = 0; dis[l[i].to] = l[i].w; getdis(l[i].to,x); for(int j = rem[0]; j; j--) { for(int k = 1; k <= m; k++) { if(query[k] >= rem[j] && judge[query[k] - rem[j]]){ test[k] = true; } } } for(int j = rem[0]; j; j--) { q[++tmp] = rem[j]; judge[rem[j]] = true; } } for(int i = 1; i <= tmp; i++) { judge[q[i]] = false; } return; } void solve(int x) { vis[x] = judge[0] = 1; calc(x); for(int i = head[x]; i; i = l[i].nxt) { if(vis[l[i].to])continue; sum = siz[l[i].to]; root = 0; findroot(l[i].to,0); solve(root); } } int main() { scanf("%d%d",&n,&m); int x,y,z; for(int i = 1; i < n; i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } for(int i = 1; i <= m; i++) { scanf("%d",&query[i]); } maxpart[root] = sum = n; findroot(1,0); solve(root); for(int i = 1; i <= m; i++) { if(test[i]) printf("AYE\n"); else printf("NAY\n"); } return 0; }

[Luogu] P3806 【模板】點分治1