点線ルール1

問題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;
}
 

おすすめ

転載: www.cnblogs.com/guoshaoyang/p/10994820.html