問題P3806 [テンプレート] [1]点線のルールへのソリューション
さんが任意にこれらの木はルート権限を取得ツリーに変換し、ルートRTを指定してみましょう
2つのカテゴリツリーにパスを見つけることは困難ではないルートノードrtの経路を通ってパック室温の(RTなし)の時に含むサブツリーを
前者のために、我々は$ DIS [U]を使用してU $ $ $ルート・ノード・パス長の$ $ RTにノードを表し、VへのUの経路長は$ DIS [U]である+ DIS [V] $
サブツリーの$ RTの$に含まれる$の五$パスへの$ Uの$以来、後者については、その後、私たちは木のサブツリーのルートを見つけ、その後、彼の最初のクラスパスを尋ねます
このようなパーティションのアイデアは非常に明確です
元のツリーはそれぞれ、多くの小さなサブツリーに分割され、各サブツリーのパスは第一種の解決します
点線のルール・プロセスは、各レイヤのすべての再帰プロセス合計時間は、Tは、各ポイントの合計層再帰的プロセスは、全体的な時間計算量は$ O(T * N)$であることが想定されます
しかし、ツリーはストランドに縮退場合、層の数を再帰的$ T = n個の$その後で、総時間の複雑さは$ O(N ^ 2)$です
これは明らかに余裕がないので、我々は見つけることが少なく、ここで木の層をさせる必要があり、ツリー、重心を
maxP [U](max_partの略称)で表されるノードU、ツリーの最大サイズを削除した後に生成サブツリー
ツリーの重心は、[]をmaxPそのノードの最小値
//sum是当前子树的总结点数,size[u]是以u为根的子树大小
void getrt(int u,int fa)
{
size[u]=1; maxp[u]=0;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==fa||vis[v])continue;
getrt(v,u);//先递归得到子树大小
size[u]+=size[v];
maxp[u]=max(maxp[u],size[v]);//更新u结点的maxp
}
maxp[u]=max(maxp[u],sum-size[u]);
if(maxp[u]<maxp[rt]) rt=u;//更新当前子树的重心
}
我々は、治療のコースに点在サブツリールート治療のための重心の各選択されたサブツリーをので、全体的な再帰の深さを超えない $ logN個$ 層の時間の複雑さを、隔壁の全体のポイントは、$ Oことを保証します(NlogN)$
(アップデート:コメントエリアで見つかった髪を折り返す前に、問題の3.1ソリューションは、Calcは理解していなかった、ここで言いたいことがたくさんあるので、その後、いくつかを追加しよう)
戻るこの質問に、プロセスパーティションに直接オフライン記録と処理を求めて
現在のルートを設定すると、現在$ v_iを$を処理しているの$ RTの$、彼のサブツリーの$ V_1、V_2 ...... V_Tの$、サブツリーを想定している、$ V_Iは$ $ $ $ RTに各ノードから取得し、 DIS $と$レム$配列に格納
注文$ジャッジ[DIS] $サブツリーを表す$ V_1 $〜$ V_ {I-1} RT $ $ $距離DIS $内のノードが存在するかどうか$
各尋問オフラインレコードを横断する、現在の現在の距離がRTにV_I $ $内のノードのサブツリーに$クエリ[k]は$、$トラバースがあるかどうかを尋ねる、各チャレンジのサブツリーのREMの$ $を横断$距離の$ REM [J] $、$ジャッジ[クエリ[K] -rem [J]] == 1 $、これはクエリのパスを表す場合に存在します
この動作を説明する特定のポイント意味は、次に、長さが存在するかどうかを確認し、ノードにオフペアサブツリー$ V_1 $〜$ V_ {I-1} $内のノードのV_I $ $サブツリーを使用することです$クエリ[k]は$パスへ
サブツリーREM $ $をペアリングのようなツリー(すなわち、$ $ $ RTサブツリーの各ノードにDIS $ $ $ V_I)次サブツリーの$ V_ {Iに進み、裁判官$ $アレイに一緒に格納された後1} $プロセス
ツリーが裁判官の配列を空にした後、室温・問い合わせ時に根ざし、その後、他のサブツリーのパーティションに
//niiick
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int inf=10000000;
const int maxn=100010;
int n,m;
struct node{int v,dis,nxt;}E[maxn<<1];
int tot,head[maxn];
int maxp[maxn],size[maxn],dis[maxn],rem[maxn];
int vis[maxn],test[inf],judge[inf],q[maxn];
int query[1010];
int sum,rt;
int ans;
void add(int u,int v,int dis)
{
E[++tot].nxt=head[u];
E[tot].v=v;
E[tot].dis=dis;
head[u]=tot;
}
void getrt(int u,int pa)
{
size[u]=1; maxp[u]=0;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==pa||vis[v]) continue;
getrt(v,u);
size[u]+=size[v];
maxp[u]=max(maxp[u],size[v]);
}
maxp[u]=max(maxp[u],sum-size[u]);
if(maxp[u]<maxp[rt]) rt=u;
}
void getdis(int u,int fa)
{
rem[++rem[0]]=dis[u];
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==fa||vis[v])continue;
dis[v]=dis[u]+E[i].dis;
getdis(v,u);
}
}
void calc(int u)
{
int p=0;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(vis[v])continue;
rem[0]=0; dis[v]=E[i].dis;
getdis(v,u);//处理u的每个子树的dis
for(int j=rem[0];j;--j)//遍历当前子树的dis
for(int k=1;k<=m;++k)//遍历每个询问
if(query[k]>=rem[j])
test[k]|=judge[query[k]-rem[j]];
//如果query[k]-rem[j]d的路径存在就标记第k个询问
for(int j=rem[0];j;--j)//保存出现过的dis于judge
q[++p]=rem[j],judge[rem[j]]=1;
}
for(int i=1;i<=p;++i)//处理完这个子树就清空judge
judge[q[i]]=0;//特别注意一定不要用memset,会T
}
void solve(int u)
{
//judge[i]表示到根距离为i的路径是否存在
vis[u]=judge[0]=1; calc(u);//处理以u为根的子树
for(int i=head[u];i;i=E[i].nxt)//对每个子树进行分治
{
int v=E[i].v;
if(vis[v])continue;
sum=size[v]; maxp[rt=0]=inf;
getrt(v,0); solve(rt);//在子树中找重心并递归处理
}
}
int main()
{
n=read();m=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read(),dis=read();
add(u,v,dis);add(v,u,dis);
}
for(int i=1;i<=m;++i)
query[i]=read();//先记录每个询问以离线处理
maxp[rt]=sum=n;//第一次先找整棵树的重心
getrt(1,0);
solve(rt);//对树进行点分治
for(int i=1;i<=m;++i)
{
if(test[i]) printf("AYE\n");
else printf("NAY\n");
}
return 0;
}