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

luoguP3806 【模板】點分治1 [點分治]

ons 路徑 col clu return 開頭 表示 否則 class

題目背景

感謝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


套用點分治的模板。

設num[k]為樹中長度為k的路徑的出現次數。

對於一個點no,可以dfs遍歷一遍以它為根的子樹中、以它開頭向下的鏈長度。

對於每一對以no開頭的鏈長度d[i],d[j],num[d[i]+d[j]]++即可。

當然要記得容斥,因為要去掉d[i],d[j]表示的鏈來自no的同一個兒子。

設no與一個兒子相連邊的長度為dis,在遞歸處理這個兒子的時候,對兒子的每一對d[i],d[j],做num[d[i]+d[j]+dis*2]--即可。

  1 #include<cstdio>
  2 #include<cstring>
  3
#include<iostream> 4 #define rint register int 5 using namespace std; 6 7 int read(){ 8 char ch; 9 int re=0; 10 bool flag=0; 11 while((ch=getchar())!=-&&(ch<0||ch>9)); 12 ch==-?flag=1:re=ch-0; 13 while((ch=getchar())>=0
&&ch<=9) re=re*10+ch-0; 14 return flag?-re:re; 15 } 16 17 struct Edge{ 18 int to,nxt,w; 19 Edge(int to=0,int nxt=0,int w=0): 20 to(to),nxt(nxt),w(w){} 21 }; 22 23 const int maxn=10005; 24 25 int n,m,cnt=0,sum,tot,root; 26 int head[maxn],son[maxn],F[maxn],num[10000005],d[maxn]; 27 bool vis[maxn]; 28 Edge E[maxn<<1]; 29 30 inline void a_ed(int from,int to,int w){ 31 E[++cnt]=Edge(to,head[from],w); 32 head[from]=cnt; 33 E[++cnt]=Edge(from,head[to],w); 34 head[to]=cnt; 35 } 36 37 void init(){ 38 n=read(); m=read(); 39 for(rint i=1,from,to,w;i<n;i++){ 40 from=read(); to=read(); w=read(); 41 a_ed(from,to,w); 42 } 43 } 44 45 void getroot(int no,int fa){ 46 son[no]=1; F[no]=0; 47 for(rint e=head[no];e;e=E[e].nxt){ 48 int nt=E[e].to; 49 if(nt==fa||vis[nt]) continue; 50 getroot(nt,no); 51 son[no]+=son[nt]; 52 F[no]=max(F[no],son[nt]); 53 } 54 F[no]=max(F[no],sum-son[no]); 55 if(F[no]<F[root]) root=no; 56 } 57 58 void getdeep(int no,int fa,int dd){ 59 d[tot++]=dd; 60 for(rint e=head[no];e;e=E[e].nxt){ 61 int nt=E[e].to; 62 if(nt==fa||vis[nt]) continue; 63 getdeep(nt,no,dd+E[e].w); 64 } 65 } 66 67 void calc(int no,bool opt,int p){ 68 tot=0; 69 getdeep(no,0,0); 70 for(int i=0;i<tot;i++) 71 for(int j=0;j<tot;j++) 72 if(opt) num[d[i]+d[j]]++; 73 else num[d[i]+d[j]+p]--; 74 } 75 76 void solve(int no){ 77 vis[no]=1; 78 calc(no,1,0); 79 for(rint e=head[no];e;e=E[e].nxt){ 80 int nt=E[e].to; 81 if(vis[nt]) continue; 82 calc(nt,0,E[e].w<<1); 83 sum=son[nt]; root=0; 84 getroot(nt,0); 85 solve(nt); 86 } 87 } 88 89 int main(){ 90 init(); 91 sum=F[root=0]=n; 92 getroot(1,0); 93 solve(root); 94 for(rint i=0,q;i<m;i++){ 95 q=read(); 96 if(num[q]) puts("AYE"); 97 else puts("NAY"); 98 } 99 return 0; 100 }

luoguP3806 【模板】點分治1 [點分治]