树链剖分 树

这里写图片描述
很明显一点,T1树里每一条边都会被选取一次。把T2树的每一条边看成一个线段覆盖。每次找到一个只被覆盖了一次的线段,找到他是被那个区间覆盖的,把那个区间删去。如果最后能删完,就有解,删不完就是无解。
搞个树剖维护区间被覆盖的最小次数。但是较难的地方是:如何判断某一条边是被那个区间覆盖的。其实我们可以再维护一个值,把覆盖这个点的区间的编号加起来,因为我们找线段时只是找被覆盖一次的线段,所以一定就是他的编号了。因此在删区间的时候,额外维护这个变量。
另一个比较难的地方:找到你要删的那条边后,把它赋成inf,他就不会对答案产生影响了(维护的是区间最小值,0可是比1小的。。),我曾经卡在一个地方,就是在删区间时删出来一个0怎么办。那么这个0一定不会再被那一个区间选择了。但是很明显每条边都是要被选的,那这就一定无解了。
注:把边权下放到点时注意把用不到的点赋成无限大。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define N 200005
#define inf 1000000000
#define mem(x) (memset(x,0,sizeof(x)))
#define ll long long
using namespace std;
struct road{int v,next;}lu[N*2];
struct node{int x,y;}a[N];
int T,ans,n,e,cnt,adj[N],f[N],top[N],sz[N],son[N],id[N],dep[N];
void add(int u,int v){lu[++e]=(road){v,adj[u]};adj[u]=e;}
namespace TREE
{
    struct tree{int l,r,su,lsu;ll h,lh;}t[N*4];
    inline void build(int l,int r,int x)
    {
        t[x].l=l;t[x].r=r;
        t[x].h=t[x].lh=t[x].lsu=t[x].su=0;
        if(l==r)return;
        int mid=l+r>>1;
        build(l,mid,x*2);
        build(mid+1,r,x*2+1);
    }
    inline void down(int x)
    {
        int h=t[x].lh,su=t[x].lsu;
        t[x].lh=t[x].lsu=0;
        t[x*2].h+=h;t[x*2].lh+=h;
        t[x*2+1].h+=h;t[x*2+1].lh+=h;
        t[x*2].su+=su;t[x*2].lsu+=su;
        t[x*2+1].su+=su;t[x*2+1].lsu+=su;
    }
    inline void Q(int x)
    {
        if(t[x].l==t[x].r){ans=t[x].h,t[x].su=inf;return;}
        if(t[x].lh)down(x);
        if(t[x*2].su==1)Q(x*2);
        else Q(x*2+1);
        t[x].su=min(t[x*2].su,t[x*2+1].su);
    }
    inline void add(int l,int r,int k,ll h,int x)
    {
        if(t[x].l>=l&&t[x].r<=r)
        {
            t[x].su+=k;t[x].lsu+=k;
            t[x].h+=h;t[x].lh+=h;
            if(l==1)t[x].su=inf;
            return;
        }
        if(t[x].lh)down(x);
        int mid=t[x].l+t[x].r>>1;
        if(l<=mid)add(l,r,k,h,x*2);
        if(r>mid)add(l,r,k,h,x*2+1);
        t[x].su=min(t[x*2].su,t[x*2+1].su);
    }
    inline void C(int x,int y,int k,ll h)
    {
        int fx=top[x],fy=top[y];
        while(fx^fy)
        {
            if(dep[fx]<dep[fy])swap(fx,fy),swap(x,y);
            add(id[fx],id[x],k,h,1);
            x=f[fx],fx=top[x];
        }
        if(x==y)return;
        if(id[x]>id[y])swap(x,y);
        add(id[x]+1,id[y],k,h,1);
    }
}
using namespace TREE;
inline void dfs1(int x,int fa)
{
    f[x]=fa;sz[x]=1;dep[x]=dep[fa]+1;
    for(int i=adj[x];i;i=lu[i].next)
    {
        int to=lu[i].v;if(to==fa)continue;
        dfs1(to,x);sz[x]+=sz[to];
        if(sz[to]>sz[son[x]])son[x]=to;
    }
}
inline void dfs2(int x,int y)
{
    top[x]=y;id[x]=++cnt;
    if(!son[x])return;
    dfs2(son[x],y);
    for(int i=adj[x];i;i=lu[i].next)
    {
        int to=lu[i].v;
        if(to==f[x]||to==son[x])continue;
        dfs2(to,to);
    }
}
bool check()
{
    dfs1(1,0);dfs2(1,1);build(1,n,1);add(1,1,1,inf,1);
    for(int i=1;i<n;i++)C(a[i].x,a[i].y,1,i);
    for(int i=1;i<n;i++)
    {
        if(t[1].su!=1)return 0;
        ans=0;Q(1);
        C(a[ans].x,a[ans].y,-1,-ans);
    }
    return 1;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);e=cnt=0;
        mem(sz);mem(son);mem(f);mem(top);mem(adj);mem(dep);mem(id);
        for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
        for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),a[i]=(node){x,y};
        if(check())printf("YES\n");
        else printf("NO\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值