Dotted rule 1

Solution to a problem P3806 [template] [1] dotted rule

Let's arbitrarily designated a root rt, these trees converted into a rooted tree

Not difficult to find a path into two categories tree, through the path of the root node rt and pack a subtree containing at rt's (without rt) of

For the former, we use $ DIS [u] u $ $ $ represents the node to the root node path length $ $ RT, the path length of u to v is the $ dis [u] + dis [v] $

For the latter, since the $ u $ to $ v $ path contained within a sub-tree $ rt $, and then we find the root of the tree subtree, and then ask him a first class path

Such partition idea is quite clear

The original tree is divided into many smaller sub-tree, respectively, and each subtree path Solving the First Kind

Dotted rule process, each layer all recursive process total time, T is assumed that a total layer recursive process for each point, the overall time complexity is $ O (T * N) $

However, if the tree degenerates into a strand , the number of layers is then recursively $ T = n $, the total time complexity is $ O (N ^ 2) $

This obviously can not afford, so we have to let the tree layers by less here to find a tree, the center of gravity

maxp [u] (max_part abbreviation) represented by the subtree produced after deleting the node u, the maximum size of the tree

The center of gravity of the tree is maxp [] that the minimum value of the node

//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;//更新当前子树的重心
}

We dotted the course of treatment each selected sub-tree of the center of gravity for the sub-tree root treatment , so the overall recursion depth does not exceed $ logN $ layer time complexity, the whole point of partition will ensure that the $ O (NlogN) $


(Update: 3.1 solution to a problem before turning back hair found in the comments area has a lot to say here calc did not understand, so then try to add some)

Back to this question, asking offline recording and processing directly in the process partition

Set the current root is $ rt $, his subtree $ v_1, v_2 ...... v_t $, assuming subtree is currently processed $ v_i $, $ V_I obtains from each node to $ $ $ a $ RT dis $ and stored in $ rem $ array

Order $ judge [dis] $ denotes the subtree $ v_1 $ ~ $ v_ {i-1} $ whether there is a node in the RT $ $ $ distance DIS $

Traversing each interrogation offline record, current traversing a subtree REM $ $ for each challenge, asking if the current distance is $ query [k] $, $ traverse to the subtree of a node in V_I $ $ to RT $ distance $ rem [j] $, if $ judge [query [k] -rem [j]] == 1 $, this represents query path exists

Specific point meaning explain this operation, then, is to use a V_I $ $ subtree of nodes in subtree $ v_1 $ ~ $ v_ {i-1} $ paired off in a node, check if there are length to $ query [k] $ path

After the tree like pairing subtree REM $ $ (i.e., to each node of $ $ $ RT subtree DIS $ $ $ V_I) is stored together into Judge $ $ array, continue with the next subtree $ v_ {i +1} $ process

When the tree rooted at rt inquiry after emptying judge array, and then to other sub-tree partition

//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;
}
 

Guess you like

Origin www.cnblogs.com/guoshaoyang/p/10994820.html