Focus luogu5666 problem solution [tree]

CSP-S2 2019 D2T3

T2 threw to fight this problem in part on the test points, and then did not see the data range is equal sign, I do not know how complete binary tree and then sentenced to 40 points thick roll ......

----

Ideas analysis

It is easy to think of $ O (n ^ 2) $ violent each time to find the center of gravity, this violence can be optimized using a variety of methods gods.

By analyzing the 35 points of special sub-structure, you can have an idea, since the special structure may have concluded, does it also can have some conclusions to solve or optimize the solution of the whole problem. In fact, some properties can be obtained by analyzing the sample, these properties may be beneficial to solve the problem:

1. The center of gravity of a tree, if there are two, the two must be adjacent to the center of gravity
2. The center of gravity of a certain tree in the root node of the heavy chain
3. The center of gravity of a certain tree is the root node the center of gravity of the sub-tree root of the ancestors of heavy son

These properties can save a lot of unnecessary time under. Depending on the nature 1, we can first find greater depth of focus, and then determine whether the father is also the center of gravity (and therefore went on to say the center of gravity is greater depth of focus) has been calculated for the center of gravity; depending on the nature 2, we when you can find the center of gravity only to find the root node of the heavy chain; depending on the nature 3, we can find the center of gravity from the bottom up, not every re looking for.

There are some properties, followed by a detailed analysis for each case.

Deleted after a tree becomes one side of two parts, two end points of the set side is $ x, y $, which is a little greater depth $ Y $, respectively, to the two parts of $ Y $ and the entire subtree rooted at $ Y $ subtracted tree rooted subtree. We first analyzed to $ y $ is the root of the subtree.

To $ Y $ subtree rooted

3 we can pretreated depending upon the nature of all the nodes in the subtree rooted at the center of gravity, can take as long as continuous up, the time complexity $ O (n) $.

To lose the entire tree rooted at $ Y $ subtree

The nature of the heavy chain 2, the center of gravity must be in the tree root node is located. Just think, as long as deleted to $ y $ subtree rooted at the same size as, in fact, is the center of gravity does not affect the position of the only possible impact of this situation is deleted in the sub-tree root node is the root of re-son subtree. We will analyze these specific circumstances again.

$ Y $ is not to re-root son rooted subtree

This is the most common situation. Since only delete the sub-tree of the size of the center of gravity of an impact, we can pre-focus after deleting a sub-tree of all sizes, then directly asked. The only pre-processing from the root node to the heavy chain from the can walk again, the time complexity $ O (n) $.

$ Y $ in order to re-root son rooted subtree

Just think, if deleted to $ y $ is the root of sub-tree, the weight is still heavy son son, then still in the original focus on this heavy chain, but apparently only possible to focus on the heavy chain away from the $ y $ one end of the movement, and then if the root node is the center of gravity, delete sub-tree root node will still be the center of gravity. Therefore, we can begin to make the most of the center of gravity of the whole tree as the root node, deal with this situation it becomes very convenient.

If the sub-tree delete this heavy weight is no longer the son of a son how to do? Clearly, now it is the only son of the heavy weight of the son of the original times. Thus, we omitted the center of gravity may be pretreated after all subtrees on the size of the heavy chain Ci son's weight, and then treated to $ Y $ subtree root node is not the root weight son in the case.

All pretreatment are $ O (n) $, each asking $ O (1) $, the overall complexity $ O (n) $.

Embodied are given below. ( Because I am too much food, the process may achieve constant is relatively large, do not imitate )

Implementation

1. Find the center of gravity

dfs again just to find a center of gravity. As the center of gravity of the whole tree after the root node.

void findroot(int x,int f)
{
    siz[x]=1;
    for(int i=head[x],y=ver[i];i;i=Next[i],y=ver[i])
        if(y!=f)
        {
            findroot(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[son[x]])
                son[x]=y;
        }
    if(siz[son[x]]*2<=n && (n-siz[x])*2<=n)
        root=x;
}

findroot(1,0);

2. Pretreatment node information

Pretreatment sub-tree for each node size, weight son, depth, father and son belong to the sub-tree root which, while finding time heavy son of the root node. dfs it again.

void pre(int x,int f)
{
    siz[x]=1,d[x]=d[f]+1,fa[x]=f;//分别表示子树大小、深度以及父亲
    if(f==root)
        ffa[x]=x;
    else
        ffa[x]=ffa[f];//属于根节点的哪个子树
    for(int i=head[x],y=ver[i];i;i=Next[i],y=ver[i])
        if(y!=f)
        {
            pre(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[son[x]])
            {
                if(x==root)
                    son2=son[x];
                son[x]=y;//找重儿子
            }
            else
                if(x==root && siz[y]>siz[son2])
                    son2=y;//找根节点的次重儿子
        }
}

memset(son,0,sizeof(son));
pre(root,0);

3.预处理答案

分别预处理出根节点重儿子所在的重链的答案、根节点次重儿子所在的重链的答案以及以每个节点为根的子树的重心。每种情况从下到上走一遍即可。

第一种情况,对于每个删掉子树大小$y$,应该在根节点重儿子所在的重链上找到深度最大的$x$满足$2siz_x\geq n-y$。第二种情况同理。

第三种情况,对于每个子树的根节点$y$,应该其子树内深度最大的$x$满足$siz[x]\geq siz[y]$。

void get1(int x,int f)
{
    if(son[x])
        get1(son[x],x);//有重儿子走重儿子
    while(n-2*siz[x]<=nowans && nowans)
    {
        ans1[nowans]=x;
        nowans--;
    }
}//根节点重儿子所在的重链的答案

void get2(int x,int f)
{
    if(x==root)
        nowans=n,get2(son2,x);//走根节点次重儿子
    else
        if(son[x])
            get2(son[x],x);//其余节点有重儿子走重儿子
    while(n-2*siz[x]<=nowans && nowans)
    {
        ans2[nowans]=x;
        nowans--;
    }
}//根节点次重儿子所在的重链的答案


void get3(int r)
{
    if(son[r])
        get3(son[r]);//先走重儿子
    for(int i=head[r],y=ver[i];i;i=Next[i],y=ver[i])
        if(y!=son[r] && d[y]>d[r])
            get3(y);
    int now=son[r]?ans3[son[r]]:r;//从重儿子的重心往上找
    while(siz[now]*2<siz[r])
        now=fa[now];
    ans3[r]=now;
}//以r为根的子树的重心

get1(root,0),get2(root,0),get3(root);

4.枚举删边求答案

实际上枚举删掉子树也是可以的,这里还是用删边来写。

按照前面说到的情况,对于每种情况,先找到一个重心,然后再判断其父亲是否也是重心。判断的时候只要按照重心的定义来判断即可,注意有些特殊的节点要进行特判。

for(int i=1;i<=tot;i+=2)
{
    int x=ver[i],y=ver[ano];
    if(d[x]>d[y])
        swap(x,y);//令y为深度较大的节点
    int h1=ans3[y];
    ans+=h1;//h1就是以y为根的子树的重心
    if(d[fa[h1]]>=d[y] && check1(fa[h1],y))
        ans+=fa[h1];//判断h1的父亲是否也是以y为根的子树的重心
    if(ffa[y]==son[root])//y在以根节点重儿子为根的子树中
        if(siz[son[root]]-siz[y]>=siz[son2])
            ans+=root;//不影响重链,重心为根节点
        else
        {
            ans+=ans2[siz[y]];
            if(check2(fa[ans2[siz[y]]],y))
                ans+=fa[ans2[siz[y]]];
        }//影响重链
    else//y不在以根节点重儿子为根的子树中
    {
        ans+=ans1[siz[y]];
        if(check3(fa[ans1[siz[y]]],y))
            ans+=fa[ans1[siz[y]]];
    }
}

(感觉好多地方可以整合在一起写……凑合着看吧qwq)

下面给出完整代码:

//40分暴力
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define ano ((i-1)^1)+1
using namespace std;
const int N=3e5+100;
int T,n,cnt,tot;
ll ans;
int head[N],ver[2*N],Next[2*N];
int maxson[N],siz[N],d[N],nowh[N],nowsiz[N];
bool sp[N],v[N];
void add(int x,int y)
{
    ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
    ver[++tot]=x,Next[tot]=head[y],head[y]=tot;
}
void dfs(int x,int root)
{
    if(x!=root)
        nowsiz[x]=1;
    v[x]=1;
    for(int i=head[x];i;i=Next[i])
        if(!sp[ver[i]] && !v[ver[i]])
        {
            int y=ver[i];
            dfs(y,root);
            maxson[x]=max(maxson[x],nowsiz[y]);
            if(x!=root)
                nowsiz[x]+=nowsiz[y];
        }
    if(maxson[x]*2<=nowsiz[root] && (nowsiz[root]-nowsiz[x])*2<=nowsiz[root])
        nowh[++cnt]=x;
}//找重心
void pre(int x,int f,int root)
{
    siz[x]=1,d[x]=d[f]+1;
    for(int i=head[x];i;i=Next[i])
        if(!d[ver[i]] && ver[i])
        {
            int y=ver[i];
            if(x==root)
                ffa[y]=y;
            else
                ffa[y]=ffa[f];
            pre(y,x,root);
            siz[x]+=siz[y];
        }
}//预处理子树大小和深度
void clearly()
{
    memset(maxson,0,sizeof(maxson));
    memset(nowsiz,0,sizeof(nowsiz));
    memset(v,0,sizeof(v));
    cnt=0;
}
void solve()
{
    pre(1,0,1);
    for(int i=1;i<=tot;i+=2)
    {
        clearly();
        int x=ver[i],y=ver[ano];
        sp[x]=sp[y]=1;//dfs时不走x,y
        if(d[x]>d[y])
            swap(x,y);//令y为深度较大的节点
        nowsiz[x]=n-siz[y],nowsiz[y]=siz[y];
        dfs(x,x),dfs(y,y);
        sp[x]=sp[y]=0;//还原
        for(int j=1;j<=cnt;j++)
            ans+=nowh[j];
    }
}
void clear()
{
    memset(head,0,sizeof(head));
    memset(Next,0,sizeof(Next));
    memset(siz,0,sizeof(siz));
    memset(d,0,sizeof(d));
    tot=ans=0;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        clear();
        scanf("%d",&n);
        for(int i=1,x,y;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        solve();
        printf("%lld\n",ans);
    }
    return 0;
}
//100分
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define ano ((i-1)^1)+1
using namespace std;
const int N=3e5+100;
int T,n,tot,root,son2,nowans;
ll ans;
int head[N],ver[2*N],Next[2*N];
int siz[N],son[N],ans1[N],ans2[N],ans3[N],ffa[N],d[N],od[N],fa[N];
void add(int x,int y)
{
    ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
    ver[++tot]=x,Next[tot]=head[y],head[y]=tot;
}
void findroot(int x,int f)
{
    siz[x]=1;
    for(int i=head[x],y=ver[i];i;i=Next[i],y=ver[i])
        if(y!=f)
        {
            findroot(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[son[x]])
                son[x]=y;
        }
    if(siz[son[x]]*2<=n && (n-siz[x])*2<=n)
        root=x;
}//找重心
void pre(int x,int f)
{
    siz[x]=1,d[x]=d[f]+1,fa[x]=f;
    if(f==root)
        ffa[x]=x;
    else
        ffa[x]=ffa[f];
    for(int i=head[x],y=ver[i];i;i=Next[i],y=ver[i])
        if(y!=f)
        {
            pre(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[son[x]])
            {
                if(x==root)
                    son2=son[x];
                son[x]=y;
            }
            else
                if(x==root && siz[y]>siz[son2])
                    son2=y;
        }
}//预处理节点信息
void get1(int x,int f)
{
    if(son[x])
        get1(son[x],x);
    while(n-2*siz[x]<=nowans && nowans)
    {
        ans1[nowans]=x;
        nowans--;
    }
}
void get2(int x,int f)
{
    if(x==root)
        nowans=n,get2(son2,x);
    else
        if(son[x])
            get2(son[x],x);
    while(n-2*siz[x]<=nowans && nowans)
    {
        ans2[nowans]=x;
        nowans--;
    }
}
void get3(int r)
{
    if(son[r])
        get3(son[r]);
    for(int i=head[r],y=ver[i];i;i=Next[i],y=ver[i])
        if(y!=son[r] && d[y]>d[r])
            get3(y);
    int now=son[r]?ans3[son[r]]:r;
    while(siz[now]*2<siz[r])
        now=fa[now];
    ans3[r]=now;
}//预处理答案
void clear()
{
    memset(head,0,sizeof(head));
    memset(Next,0,sizeof(Next));
    memset(ver,0,sizeof(ver));
    memset(son,0,sizeof(son));
    nowans=n,son2=tot=ans=0;
}
bool check1(int x,int y)
{
    return x && siz[son[x]]*2<=siz[y] && (siz[y]-siz[x])*2<=siz[y];
}
bool check2(int x,int y)
{
    if(x==root)
        return siz[son2]*2<=n-siz[y];
    return x && siz[son[x]]*2<=n-siz[y] && (n-siz[y]-siz[x])*2<=n-siz[y];
}
bool check3(int x,int y)
{
    if(x==root)
        return siz[son[x]]*2<=n-siz[y];
    return x && siz[son[x]]*2<=n-siz[y] && (n-siz[y]-siz[x])*2<=n-siz[y];
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        clear();
        for(int i=1,x,y;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        findroot(1,0);
        memset(son,0,sizeof(son));
        pre(root,0),get1(root,0),get2(root,0),get3(root);
        for(int i=1;i<=tot;i+=2)
        {
            int x=ver[i],y=ver[ano];
            if(d[x]>d[y])
                swap(x,y);
            int h1=ans3[y];
            ans+=h1;
            if(d[fa[h1]]>=d[y] && check1(fa[h1],y))
                ans+=fa[h1];
            if(ffa[y]==son[root])
                if(siz[son[root]]-siz[y]>=siz[son2])
                    ans+=root;
                else
                {
                    ans+=ans2[siz[y]];
                    if(check2(fa[ans2[siz[y]]],y))
                        ans+=fa[ans2[siz[y]]];
                }
            else
            {
                ans+=ans1[siz[y]];
                if(check3(fa[ans1[siz[y]]],y))
                    ans+=fa[ans1[siz[y]]];
            }
        }//枚举删边求答案
        printf("%lld\n",ans);
    }
    return 0;
}

Guess you like

Origin www.cnblogs.com/TEoS/p/11938664.html