[CodeChef EDGEST] Edges in Spanning Trees (+ tree chain split heuristic merge tree)

Click here to see the problem surface

Roughly meaning of the questions: to give you two \ (n \) points of the tree, the first tree for each edge \ (e_1 \) , ask how many sides of the second tree in the presence \ (e_2 \) , such that the first tree delete \ (E_1 \) plus \ (E_2 \) , the second tree delete \ (E_2 \) plus \ (E_1 \) after all spanning trees remains.

Transformed the meaning of problems

Consider for \ (E_1 (X, Y) \) , legal \ (E_2 (U, V) \) , necessarily present in the second tree in \ (X \) to \ (Y \) over the path.

Similarly, the \ (E_1 \) should also be present in the first tree in \ (U \) to \ (V \) over the path.

Given that we are in the enumeration \ (e_1 \) , and each side can be expressed as \ ((x, fa_x) \ ) form.

That is, if \ (E_1 \) is present in the first tree in \ (U \) to (V \) \ above the path, the \ (u, v \) should be present in a \ (X \) within the sub-tree, in a \ (X \) outer subtree.

So the question becomes is intended, for each of the first tree in a dot \ (X \) , provided that the first parent node in the tree is \ (fa_x \) , seeking in the second tree \ (X \) to \ (fa_x \) path exists many edges \ ((U, V) \) , satisfies \ (u, v \) that exists in \ (X \) within the sub-tree, one \ (X \) outside the sub-tree.

Chain + tree split heuristic merge tree

Consider a second tree for tree chain subdivision of a tree for a tree heuristic merge .

We deal with each of the \ (X \) , the case of the second cross-sectional line tree tree tree, the first tree \ (X \) subtree the point \ (. 1 \) , \ (X \ ) subtree outer point \ (0 \) .

Then we can cross plucked out of the second tree tree in \ (x \) to its first tree in his father's path, and then calculated how many pair of adjacent \ (01 \) .

When you merge two intervals, we record the answer as long as the value of its left and right ends of each interval, and then compare the right and left sections is the same to the left and right sections.

Note tree cutout cross paths when some details that need attention, for the process to jump up two points to statistics separately, and finally merge the two points up first will be a point where the left and right ends of the exchange value of the merger when it re-jump interval.

Code

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define LL long long
#define RL Reg LL
#define CL Con LL&
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n;struct edge {int to,nxt;};
class FastIO
{
    private:
        #define FS 100000
        #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
        #define pc(c) (C==E&&(clear(),0),*C++=c)
        #define tn (x<<3)+(x<<1)
        #define D isdigit(c=tc())
        int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    public:
        I FastIO() {A=B=FI,C=FO,E=FO+FS;}
        Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
        Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
        Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
        Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
        I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
class Tree1//题意中的第二棵树,树链剖分
{
    private:
        int ee,d,lnk[N+5],Sz[N+5],son[N+5],fa[N+5],dep[N+5],dfn[N+5],Top[N+5];edge e[N<<1];
        struct Il//区间
        {
            int L,R,S;I Il(CI x=0,CI y=0,CI v=0):L(x),R(y),S(v){}
            I Il operator + (Con Il& o) {return ~S?(~o.S?Il(L,o.R,S+o.S+(R^o.L)):*this):o;}//合并区间
        };
        template<int SZ> class SegmentTree//树剖线段树
        {
            private:
                #define LT l,mid,rt<<1
                #define RT mid+1,r,rt<<1|1
                #define PU(x) (O[x]=O[x<<1]+O[x<<1|1])
                int n;Il O[SZ<<2];
                I void Upt(CI x,CI v,CI l,CI r,CI rt)//单点修改
                {
                    if(l==r) return (void)(O[rt].L=O[rt].R=v);RI mid=l+r>>1;
                    x<=mid?Upt(x,v,LT):Upt(x,v,RT),PU(rt);
                }
                I Il Qry(CI tl,CI tr,CI l,CI r,CI rt)//扣区间
                {
                    if(tl==l&&r==tr) return O[rt];RI mid=l+r>>1;
                    if(tr<=mid) return Qry(tl,tr,LT);if(tl>mid) return Qry(tl,tr,RT);
                    return Qry(tl,mid,LT)+Qry(mid+1,tr,RT);
                }
            public:
                I void Build(CI _n) {n=_n;}I void Upt(CI x,CI v) {Upt(x,v,1,n,1);}
                I Il Qry(CI l,CI r) {return Qry(l,r,1,n,1);}
        };SegmentTree<N> S;
        I void dfs1(CI x)//第一遍树剖dfs
        {
            for(RI i=(Sz[x]=1,son[x]=0,lnk[x]);i;i=e[i].nxt) e[i].to^fa[x]&&
            (
                dep[e[i].to]=dep[fa[e[i].to]=x]+1,dfs1(e[i].to),
                Sz[x]+=Sz[e[i].to],Sz[e[i].to]>Sz[son[x]]&&(son[x]=e[i].to)
            );
        }
        I void dfs2(CI x,CI col)//第二遍树剖dfs
        {
            Top[x]=col,dfn[x]=++d,son[x]&&(dfs2(son[x],col),0);
            for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x]&&
                e[i].to^son[x]&&(dfs2(e[i].to,e[i].to),0);
        }
    public:
        I void Clear() {memset(lnk,0,sizeof(lnk)),ee=d=0;}I void Add(CI x,CI y) {add(x,y);}
        I void TreeChainDissection() {dfs1(1),dfs2(1,1),S.Build(n);}
        I void Upt(CI x,CI v) {S.Upt(dfn[x],v);}//单点修改
        I int Qry(RI x,RI y)//扣区间
        {
            Il tl,tr;tl.S=tr.S=-1;W(Top[x]^Top[y])
            {
                if(dep[Top[x]]>dep[Top[y]]) tl=S.Qry(dfn[Top[x]],dfn[x])+tl,x=fa[Top[x]];//记录第一个点向上跳的过程
                else tr=S.Qry(dfn[Top[y]],dfn[y])+tr,y=fa[Top[y]];//记录第二个点向上跳的过程
            }
            dfn[x]>dfn[y]?(tl=S.Qry(dfn[y],dfn[x])+tl):(tr=S.Qry(dfn[x],dfn[y])+tr);//判断是哪个点向上跳
            return swap(tl.L,tl.R),(tl+tr).S;//交换第一个点区间左右端值,然后合并
        }
}T1;
class Tree2//题意中的第一棵树,树上启发式合并
{
    private:
        int ee,lnk[N+5],Sz[N+5],son[N+5],ans[N+5];edge e[N<<1];
        I void Fill(CI x,CI lst,CI v)//把整棵子树染成v
        {
            T1.Upt(x,v);for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(Fill(e[i].to,x,v),0);
        }
        I void dfs(CI x,CI lst,CI pos)//遍历
        {
            if(son[x])//如果有子节点
            {
                RI i,p;for(i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&//处理轻儿子
                    (e[i].to^son[x]?(dfs(e[i].to,x,i+1>>1),Fill(e[i].to,x,0),0):p=i+1>>1);//处理完就染白
                for(dfs(son[x],x,p),i=lnk[x];i;i=e[i].nxt)//处理重儿子
                    e[i].to^lst&&e[i].to^son[x]&&(Fill(e[i].to,x,1),0);//然后把其他儿子重新染黑
            }T1.Upt(x,1),pos&&(ans[pos]=T1.Qry(x,lst));//把当前点染黑,计算答案
        }
    public:
        I void Clear() {memset(lnk,0,sizeof(lnk)),ee=0;}I void Add(CI x,CI y) {add(x,y);}
        I void Init(CI x,CI lst)//初始化求重儿子
        {
            for(RI i=(Sz[x]=1,son[x]=0,lnk[x]);i;i=e[i].nxt) e[i].to^lst&&
                (Init(e[i].to,x),Sz[x]+=Sz[e[i].to],Sz[e[i].to]>Sz[son[x]]&&(son[x]=e[i].to));
        }
        I void DSU_on_Tree()//树上启发式合并
        {
            dfs(1,0,0),Fill(1,0,0);//遍历,清空
            for(RI i=1;i^n;++i) F.write(ans[i]," \n"[i==n-1]);//输出答案
        }
}T2;
int main()
{
    RI Tt,i,x,y;F.read(Tt);W(Tt--)
    {
        T1.Clear(),T2.Clear(),F.read(n);//清空
        for(i=1;i^n;++i) F.read(x,y),T2.Add(x,y),T2.Add(y,x);//读入题意中的第一棵树
        for(i=1;i^n;++i) F.read(x,y),T1.Add(x,y),T1.Add(y,x);//读入题意中的第二棵树
        T1.TreeChainDissection(),T2.Init(1,0),T2.DSU_on_Tree();//求答案
    }return F.clear(),0;
}

Guess you like

Origin www.cnblogs.com/chenxiaoran666/p/CodeChefEDGEST.html