CF342E Xenia and Tree

题意

给你一棵 n n n个点的树,根节点是红点。有 m m m个操作分为两种:

  1. 把一个点染成红点。
  2. 询问一个点到最近红点的距离。

思路

分块:
  1. n n n m m m的范围是 1 e 5 1e5 1e5时限是 5 s 5s 5s n n n \sqrt{n} nn 似乎过的去。于是想到了分块。对于每个点保留一个最近距离 M i n d i s [ ] Mindis[] Mindis[],一开始是 d e p t h [ ] − 1 depth[]-1 depth[]1
    1. 将修改储存在 q [ ] q[] q[],每 n \sqrt{n} n 个修改统一跑一遍最短路。
    2. 对于询问则将询问点的最近距离和储存的修改到询问点的距离打擂。即 a n s = m i n ( M i n d i s [ q u e r y ] , d i s ( q [ 1 → n ] , q u e r y ) ) ans=min(Mindis[query],dis(q[1\to \sqrt{n}],query)) ans=min(Mindis[query],dis(q[1n ],query))这样可以保证复杂度在 n n n\sqrt{n} nn 以内。因为时间比较充裕,加个 l o g log log也是没有关系的其实是因为不会O(1)lca
树链剖分:
  1. 开始并不知道树链剖分还有这种神奇的做法,直到贺了题解。不过可惜的是题解里就讲了如何书剖,剩下的还是要靠自己yy。这里就相当于对他的做法解释一下。
    1. 对于每一条链建一棵线段树,用链顶存到子树内最近红点的距离。
    2. 对于每一棵线段树,记录下到链顶的最近红点距离和到链尾的最近红点距离。
    3. 对于每一个询问,不停跳链,进行打擂。查询到链中一点时可将线段树上比当前的浅的到链尾的最近距离减去重复走的 和比当前点深的到链顶的最近距离减去重复走的 打擂。像这样
    I void queryR(int id){
        int res=inf;
        for(int i=id;i;i=node[top].fa){
            top=node[i].top;
            sz=link[top].size();
            if(sz==1)res=min(res,node[i].dis+node[id].depth-node[i].depth);
            else{
                res=min(res,query(root[top],0,node[i].id,1)+node[i].id-(sz-1)+node[id].depth-node[i].depth);
                res=min(res,query(root[top],node[i].id,sz-1,0)-node[i].id+node[id].depth-node[i].depth);
            }
        }
        printf("%d\n",res);
    }
    
    I int query(int k,int l,int r,int op){
        if(l<=s[k].l&&s[k].r<=r){
            if(!op)return s[k].ml;
            if(op)return s[k].mr;
        }
        int mid=(s[k].l+s[k].r)>>1,ret=inf;
        if(l<=mid)ret=min(ret,query(s[k].ls,l,r,op));
        if(r> mid)ret=min(ret,query(s[k].rs,l,r,op));
        return ret;
    }
    
    1. 对于每一个修改呢,也是跳链修改。修改到链顶的最近红点距离和到链尾的最近红点距离。像这样
    I void updateR(int id){
        for(int i=id;i;i=node[top].fa){
            top=node[i].top;
            sz=link[top].size();
            if(node[i].dis>node[id].depth-node[i].depth){
                node[i].dis=node[id].depth-node[i].depth;
                if(sz>1)update(root[top],node[i].id,node[i].dis);
            }else break;
        }
    }
    
    I void update(int k,int id,int v){
        if(s[k].l==s[k].r){
            s[k].ml=s[k].l+v;
            s[k].mr=sz-1-s[k].l+v;
            return;
        }
        int mid=(s[k].l+s[k].r)>>1;
        if(id<=mid)update(s[k].ls,id,v);
        if(id> mid)update(s[k].rs,id,v);
        pushUp(k);
    }
    

然后就可以 l o g 2 log^2 log2解决问题了。

代码

分块:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define I inline
using namespace std;
typedef long long LL;
const int Mx=1e5+10086;
const int inf=1e6;
int n,q,root[Mx];

template<typename T>
I T read(){
    T ans=0,b=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')b=-1;else b=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ans=ans*10+ch-48;ch=getchar();}
    return ans*b;
}
class Tree{
public:
    I void addEdge(int x,int y){
        nex[++tot]=fir[x];fir[x]=tot;to[tot]=y;
        nex[++tot]=fir[y];fir[y]=tot;to[tot]=x;
    }
    I void init(int n){
        tot=0;
        for(int i=1;i<=n;i++)lg[i]=lg[i-1]+((1<<lg[i-1])==i);
        base=sqrt(n)+1;
    }
    I void buildTree(int x,int f){
        depth[x]=depth[f]+1;
        dis[x]=depth[x]-1;
        fa[0][x]=f;
        for(int i=1;i<=lg[depth[x]];i++)fa[i][x]=fa[i-1][fa[i-1][x]];
        for(int i=fir[x];i;i=nex[i]){
            int v=to[i];
            if(v==f)continue;
            buildTree(v,x);
        }
    }
    I void query(int id){
        int ans=dis[id];
        for(int i=1;i<=top;i++)ans=min(ans,dist(id,q[i]));
        printf("%d\n",ans);
    }
    I void update(int id){
        q[++top]=id;
        vis[id]=1;
        dis[id]=0;
        if(top==base){
            addNode();
            top=h=0;
            memset(vis,0,sizeof vis);
        }
    }
protected:
    int fir[Mx],nex[Mx<<1],to[Mx<<1],tot;
    int fa[30][Mx],depth[Mx],lg[Mx];
    int q[Mx],top,base,dis[Mx],h;
    bool vis[Mx];
    I int lca(int x,int y){
        if(depth[x]<depth[y])swap(x,y);
        while(depth[x]>depth[y])x=fa[lg[depth[x]-depth[y]]-1][x];
        if(x==y)return x;
        for(int i=lg[depth[x]];i>=0;i--){
            if(fa[i][x]!=fa[i][y]){
                x=fa[i][x];
                y=fa[i][y];
            }
        }
        return fa[0][x];
    }
    I void addNode(){
        do{
            int x=q[++h];
            for(int i=fir[x];i;i=nex[i]){
                int v=to[i];
                if(vis[v])continue;
                if(dis[v]>dis[x]+1){
                    dis[v]=dis[x]+1;
                    vis[v]=1;
                    q[++top]=v;
                }
            }
        }while(h<top);
    }
    I int dist(int x,int y){
        int f=lca(x,y);
        return depth[x]+depth[y]-depth[f]*2;
    }
}T;
I void init(){
    n=read<int>();q=read<int>();
    T.init(n);
    for(int i=1;i<n;i++)T.addEdge(read<int>(),read<int>());
    T.buildTree(1,0);
}
I void work(){
    int op,id;
    for(int i=1;i<=q;i++){
        scanf("%d%d",&op,&id);
        switch(op){
            case 1:T.update(id);break;
            case 2:T.query(id);break;
        }
    }
}
int main(int argv,char* argc[]){
    init();
    work();
}
树链剖分:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<climits>
#include<vector>
#include<iostream>
#define I inline
using namespace std;
typedef long long LL;
const int Mx=1e5+10086;
const int inf=1e6;
int n,q,root[Mx];

template<typename T>
I T read(){
    T ans=0,b=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')b=-1;else b=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ans=ans*10+ch-48;ch=getchar();}
    return ans*b;
}

class Tree{
public:
    I void addEdge(int x,int y){
        nex[++tot]=fir[x];fir[x]=tot;to[tot]=y;
        nex[++tot]=fir[y];fir[y]=tot;to[tot]=x;
    }
    I void buildTree(int x,int f){
        int MaxSon=-1;
        node[x].depth=node[f].depth+1;
        node[x].fa=f;
        node[x].size=1;
        for(int i=fir[x];i;i=nex[i]){
            int v=to[i];
            if(v==f)continue;
            buildTree(v,x);
            node[x].size+=node[v].size;
            if(node[v].size>MaxSon)node[x].son=v,MaxSon=node[v].size;
        }
    }
protected:
    struct Node{
        int fa,son,depth,id,size,top,dis;
    }node[Mx];
    int fir[Mx],nex[Mx<<1],to[Mx<<1],tot;
};
class CutTree:public Tree{
public:
    I void buildCutTree(int x,int top){
        int si=link[top].size();
        node[x].top=top;
        node[x].id=si;
        link[top].push_back(x);
        if(!node[x].son)return;
        buildCutTree(node[x].son,top);
        for(int i=fir[x];i;i=nex[i]){
            int v=to[i];
            if(v==node[x].fa||v==node[x].son)continue;
            buildCutTree(v,v);
        }
    }
    I void updateR(int id);
    I void queryR(int id);
protected:
    vector<int>link[Mx];
};
class SegTree:public CutTree{
public:
    #define ml minFromLeft
    #define mr minFromRight
    #define s segnode
    I void init(){
        for(int i=1;i<=n;i++)node[i].dis=inf;
        node[1].dis=0;
        buildTree(1,0);
        buildCutTree(1,1);
        for(int i=1;i<=n;i++){
            int si=link[i].size();
            if(node[i].top==i&&si!=1)
                buildSegTree(root[i],0,si-1);
        }
        sz=link[1].size();
        update(root[1],0,0);
    }
    I int query(int k,int l,int r,int op){
        if(l<=s[k].l&&s[k].r<=r){
            if(!op)return s[k].ml;
            if(op)return s[k].mr;
        }
        int mid=(s[k].l+s[k].r)>>1,ret=inf;
        if(l<=mid)ret=min(ret,query(s[k].ls,l,r,op));
        if(r> mid)ret=min(ret,query(s[k].rs,l,r,op));
        return ret;
    }
    I void update(int k,int id,int v){
        if(s[k].l==s[k].r){
            s[k].ml=s[k].l+v;
            s[k].mr=sz-1-s[k].l+v;
            return;
        }
        int mid=(s[k].l+s[k].r)>>1;
        if(id<=mid)update(s[k].ls,id,v);
        if(id> mid)update(s[k].rs,id,v);
        pushUp(k);
    }
    I void updateR(int id){
        for(int i=id;i;i=node[top].fa){
            top=node[i].top;
            sz=link[top].size();
            if(node[i].dis>node[id].depth-node[i].depth){
                node[i].dis=node[id].depth-node[i].depth;
                if(sz>1)update(root[top],node[i].id,node[i].dis);
            }else break;
        }
    }
    I void queryR(int id){
        int res=inf;
        for(int i=id;i;i=node[top].fa){
            top=node[i].top;
            sz=link[top].size();
            if(sz==1)res=min(res,node[i].dis+node[id].depth-node[i].depth);
            else{
                res=min(res,query(root[top],0,node[i].id,1)+node[i].id-(sz-1)+node[id].depth-node[i].depth);
                res=min(res,query(root[top],node[i].id,sz-1,0)-node[i].id+node[id].depth-node[i].depth);
            }
        }
        printf("%d\n",res);
    }
private:
    struct SegNode{
        int ls,rs,l,r,minFromLeft,minFromRight;
    }segnode[Mx<<3];
    int sz,si,top;
    I void buildSegTree(int &k,int l,int r){
        k=++si;s[k].l=l,s[k].r=r;s[k].ml=s[k].mr=inf;
        if(l==r)return;
        int mid=(l+r)>>1;
        buildSegTree(s[k].ls,l,mid);
        buildSegTree(s[k].rs,mid+1,r);
    }
    I void pushUp(int k){
        s[k].ml=min(s[s[k].ls].ml,s[s[k].rs].ml);
        s[k].mr=min(s[s[k].ls].mr,s[s[k].rs].mr);
    }
}T;

void init(){
    n=read<int>();q=read<int>();
    for(int i=1;i<n;i++)T.addEdge(read<int>(),read<int>());
    T.init();
}
void work(){
    T.update(root[1],0,0);
    int op,x;
    for(int i=1;i<=q;i++){
        scanf("%d%d",&op,&x);
        if(op==1)T.updateR(x);
        if(op==2)T.queryR(x);
    }
}

int main(int argv,char* argc[]){
    init();
    work();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值